You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于生理信号的Keras多分类模型始终预测同一标签问题求助

Hey there, let's break down why your CNN is stuck with low accuracy and predicting the same class—there are a few key issues in your data handling and model setup that are likely causing this. Let's go through them one by one:

1. Fix Your Label Handling (Critical!)

Looking at your code, you're reshaping train_y into (312, 18000) and then taking only the first column as the label. But if each of your 312 records corresponds to a single emotion label (which makes sense for a subject's 18000-row signal), your train_y should be a 1D array of shape (312,), not (312, 18000).

If your original train_y has 18000 duplicate labels per record, you're not technically breaking things by taking the first one, but this suggests you might have misloaded the labels. Double-check that each subject's label is stored once, not repeated for every data point in their signal. Your final train_y_enc should be (312, 6) (one hot-encoded label per sample), which it might be now—but confirming this is foundational.

2. Normalize Your Physiological Signals (Non-Negotiable!)

ECG, GSR, and temperature have wildly different value ranges. For example, ECG is typically in the millivolt range (~0-1mV), temperature is ~36-38°C, and GSR can be anywhere from 1-100 microSiemens. Without normalization, the model will prioritize features with larger numerical values completely ignoring the others, leading to failed learning.

Add this preprocessing step before reshaping:

from sklearn.preprocessing import MinMaxScaler

# Min-Max scale to [0,1] (ideal for CNNs as it keeps signal patterns intact)
scaler = MinMaxScaler()
train_x_scaled = scaler.fit_transform(train_x)

# Then reshape as before
train_x = train_x_scaled.reshape(312, 18000, 3)
3. Overhaul Your CNN Architecture

Your current model is way too underpowered and uses overly large kernels:

  • Too few filters: 2 filters per Conv1D layer is barely enough to capture any meaningful patterns in physiological signals. Start with 32, then 64, then 128 as you go deeper.
  • Huge kernel size: A 700-length kernel on an 18000-step sequence is massive—it's looking at ~38 seconds of data (assuming 18000 steps = 60s, 300Hz sampling) at once. Physiological features are local (like QRS complexes in ECG, skin conductance peaks in GSR). Use smaller kernels like 3, 5, 11, or mix sizes (e.g., small kernels first for fine details, larger ones later for broader patterns).
  • Add Batch Normalization: This stabilizes training by normalizing layer inputs, which helps with convergence.
  • Add a hidden dense layer: After pooling, a dense layer adds more capacity to map extracted features to emotion classes.

Here's a revised architecture to try:

from tensorflow.keras.layers import BatchNormalization

model = Sequential()
# Input layer: 18000 timesteps, 3 features
model.add(Conv1D(32, 11, activation='relu', input_shape=(18000, 3)))
model.add(BatchNormalization())
model.add(MaxPooling1D(2))

model.add(Conv1D(64, 5, activation='relu'))
model.add(BatchNormalization())
model.add(MaxPooling1D(2))

model.add(Conv1D(128, 3, activation='relu'))
model.add(BatchNormalization())
model.add(GlobalAveragePooling1D())

model.add(Dropout(0.5))
model.add(Dense(64, activation='relu')) # Hidden dense layer for feature mapping
model.add(Dense(6, activation='softmax'))
4. Fix Training Parameters
  • Shuffle your data!: You set shuffle=False—if your dataset is ordered by emotion class (e.g., all class 0 first, then class 1, etc.), the model will only learn the first few classes and eventually default to the most frequent one. Always shuffle training data unless you have a specific reason not to:
    model.fit(..., shuffle=True)
    
  • Optimize your optimizer: SGD can be slow to converge, especially with unnormalized data. Try Adam first (it's adaptive and works well for most cases):
    from tensorflow.keras.optimizers import Adam
    model.compile(optimizer=Adam(learning_rate=1e-4), loss='categorical_crossentropy', metrics=['acc'])
    
  • Add Early Stopping: 300 epochs is way too many if the model isn't learning properly. Stop training early once validation accuracy plateaus to avoid wasting time and overfitting:
    from tensorflow.keras.callbacks import EarlyStopping
    early_stopping = EarlyStopping(patience=10, restore_best_weights=True)
    model.fit(..., epochs=100, callbacks=[early_stopping])
    
5. Check for Class Imbalance

If one emotion class makes up most of your 312 samples (e.g., 50% are class 0), the model will learn to predict that class to minimize loss, leading to low overall accuracy and fixed predictions.

Check your class distribution with:

import pandas as pd
print(pd.Series(trunc_train_y.flatten()).value_counts())

If imbalance exists:

  • Use class weights in model.fit() to prioritize minority classes:
    from sklearn.utils.class_weight import compute_class_weight
    class_weights = compute_class_weight('balanced', classes=[0,1,2,3,4,5], y=trunc_train_y.flatten())
    class_weight_dict = dict(zip([0,1,2,3,4,5], class_weights))
    model.fit(..., class_weight=class_weight_dict)
    
6. Validate Your Data Split

With validation_split=0.33 and shuffle=False, your validation set is the last 33% of your data. If your data is ordered by class, the validation set might only contain a few classes, making your accuracy metric meaningless. Use stratified splitting to ensure both train/val sets have similar class distributions:

from sklearn.model_selection import train_test_split
X_train, X_val, y_train, y_val = train_test_split(train_x, train_y_enc, test_size=0.33, stratify=train_y_enc, random_state=42)
model.fit(X_train, y_train, validation_data=(X_val, y_val), ...)

Start with these fixes—especially normalization, shuffling, and adjusting the model architecture. These are the most likely culprits for your current performance issues.


内容的提问来源于stack exchange,提问作者Ozan Yurtsever

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 05:24:11