Python LSTM模型在多类分类中只分类1类

Python LSTM模型在多类分类中只分类1类,python,tensorflow,machine-learning,Python,Tensorflow,Machine Learning,我使用LSTM为降雨数据集的不平衡多类分类编写了python代码。输入变量是温度、日照和湿度的数字形式。目标有四类:无雨、小雨、中雨和大雨,但模型在混淆矩阵中仅分类一类,如以下代码所示 我也尝试了斯莫特和班级重量技术来平衡班级,但结果没有改变 有人能帮我完成不平衡多类分类的LSTM代码吗 混淆矩阵 数据集截图 import numpy as np import tensorflow as tf from tensorflow import keras from sklearn.utils im

我使用LSTM为降雨数据集的不平衡多类分类编写了python代码。输入变量是温度、日照和湿度的数字形式。目标有四类:无雨、小雨、中雨和大雨,但模型在混淆矩阵中仅分类一类,如以下代码所示


我也尝试了斯莫特和班级重量技术来平衡班级,但结果没有改变

有人能帮我完成不平衡多类分类的LSTM代码吗

混淆矩阵

数据集截图

import numpy as np
import tensorflow as tf
from tensorflow import keras
from sklearn.utils import resample
import pandas as pd
import seaborn as sns
from numpy import array
from numpy import argmax
from pylab import rcParams
import matplotlib.pyplot as plt
from matplotlib import rc
from keras.regularizers import l1,l2,l1_l2
from pandas.plotting import register_matplotlib_converters
from sklearn.preprocessing import RobustScaler, MinMaxScaler, StandardScaler
from tensorflow.keras.layers import Dense, SimpleRNN, LSTM, Dropout, GRU, Bidirectional, Activation
from scipy import stats
from tensorflow.keras import Sequential
from sklearn.preprocessing import OneHotEncoder
from sklearn.metrics import confusion_matrix
from imblearn.combine import SMOTETomek
from imblearn.under_sampling import NearMiss
from sklearn.utils import class_weight
%matplotlib inline
%config InlineBackend.figure_format='retina'
from sklearn.metrics import confusion_matrix
from dateutil.parser import parse
register_matplotlib_converters()
sns.set(style='whitegrid', palette='muted', font_scale=1.5)
rcParams['figure.figsize'] = 22, 10
RANDOM_SEED = 42
np.random.seed(RANDOM_SEED)
tf.random.set_seed(RANDOM_SEED)

# load dataset
df = pd.read_csv("Arbaminch_Rainfall_All_In_One_Classiffication.csv")
#df['Date'] =  pd.to_datetime(df['Date'], dayfirst=True, format='%d-%m-%Y', errors='coerce')
#df = df.set_index('Date').rename_axis('Rainfall', axis=1)
df = df.drop('Date', 1)
#df.plot()
df.head()



columns = df.columns.tolist()
# filter the columns to remove data e do not want
columns = [c for c in columns if c not in['Rainfall9AM']]
target = df.Rainfall9AM
state = np.random.RandomState(42)

X = df[columns] # independent Variable
Y = target      # dependent Variable

X.shape, Y.shape

X = X.ffillna(X.mean())
Y= Y.fillna(method='ffill')


from sklearn import preprocessing 
label_encoder = preprocessing.LabelEncoder()
Y = label_encoder.fit_transform(Y)
Y = pd.DataFrame(Y)
Y.columns = ['Rainfall9AM']





from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.33, random_state=1,stratify=Y)
X_train.shape, y_train.shape, X_test.shape, y_test.shape


#All Varibles
scale_columns = ['MaxTemp6PM', 'MinTemp9AM', 'AVGTemp', 'Sunshine6PM', 'Humidity6AM', 'Humidity9AM', 'Humidity12PM', 
                'Humidity3PM', 'Humidity6PM', 'MaxHumid', 'MinHumid', 'AVGHumid']

# Granger selected features 
#'Rainfall9AM','MaxTemp6PM','Sunshine6PM','Humidity6AM','Humidity12PM',
#    'Humidity6PM','MaxHumid','MinHumid','AVGHumid'

#scale_columns = ['MaxTemp6PM','Sunshine6PM','Humidity6AM','Humidity12PM','Humidity6PM','MaxHumid','MinHumid','AVGHumid']

scaler = MinMaxScaler(feature_range = (0,1))
scaler = scaler.fit(X_train[scale_columns])

X_train.loc[:, scale_columns] = scaler.transform(X_train[scale_columns].to_numpy())
X_test.loc[:, scale_columns] = scaler.transform(X_test[scale_columns].to_numpy())




#from sklearn.utils.class_weight import compute_class_weight
#classes = np.array([0,1,2,3])
#weights = compute_class_weight('balanced', classes, y_for_train).all()
class_weights = {
    0: 4.,
    1 :5.,
    2 :5.,
    3 :1.
}





#To create 3D for Lstm
def create_dataset(X, y, time_steps=1, step=1):
    Xs, ys = [], []
    for i in range(0, len(X) - time_steps, step):
        v = X.iloc[i:(i + time_steps)].values
        labels = y.iloc[i: i + time_steps]
        Xs.append(v)        
        ys.append(stats.mode(labels)[0][0])
        return np.array(Xs), np.array(ys).reshape(-1, 1)
TIME_STEPS = 30
STEP = 1
X_train, y_train = create_dataset(X_train, y_train, TIME_STEPS, STEP)
X_test, y_test = create_dataset(X_test, y_test, TIME_STEPS, STEP)



# One Hot Encode
enc = OneHotEncoder(handle_unknown='ignore', sparse=False)
enc = enc.fit(y_train)
y_train = enc.transform(y_train)
y_test = enc.transform(y_test)



#define model
optimizer = keras.optimizers.RMSprop(learning_rate=0.001, momentum= 0.0)
model = Sequential()
model.add(Bidirectional(LSTM(100, dropout=0.2, recurrent_dropout=0.2, activation='relu'), input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dense(y_train.shape[1], Activation('softmax'), bias_regularizer=l2(1e-2)))
model.compile(loss='categorical_crossentropy', optimizer = optimizer, metrics=['acc'])
model.summary()




history = model.fit(X_train, y_train,epochs=10,class_weight = class_weights, batch_size=32,validation_data=(X_test,y_test),shuffle=True)


model.evaluate(X_test, y_test)
y_pred = model.predict(X_test)
from sklearn.metrics import accuracy_score
# Creates a confusion matrix
num_classes = 4
y_true = tf.argmax(y_test, axis = 1)
y_pred = tf.argmax(y_pred, axis = 1)




cm = confusion_matrix(y_true, y_pred, labels=[0,1,2,3]) 
cm_df = pd.DataFrame(cm,index = ['No_Rain', 'Light_Rain', 'Moderate', 'Heavy_Rain'], columns = ['No_Rain', 'Light_Rain', 'Moderate', 'Heavy_Rain'])

plt.figure(figsize=(15,5))
sns.heatmap(cm_df, annot=True, fmt="d", cmap='Blues') 
plt.title('Bidirectional_LSTM_Model\nAccuracy:{0:.3f}'.format(accuracy_score(y_true, y_pred)))
plt.ylabel('True Label')
plt.xlabel('Predicted Label')
plt.show()

我相信您的代码中有一些bug。有一件事你没有向我解释,比如你为什么在标签上调用stats.modelabel。另外,我也不知道你是否正确使用了SMOTE等等

但是,由于您无法在这里提供有关stackoverflow的任何数据,因此我从Kaggle获取了数据集

它看起来很像你的。最大的区别是我们明天只有两节课:是和否。但这也很不平衡:

是77% 没有23% 我实现了一个简单的模型,带有一些非常懒惰的预处理,所以所有这些都可能会有很大的改进。然而,从这个示例中学习并与您的示例进行比较应该足够好了。只需下载数据并试一试

没有任何调整和平衡,我能够获得约82%的测试数据的准确性。在Kaggle上,我看到人们得到了88%的回报,所以我们肯定可以改进我们的模型

您需要做的就是确保weatherAUS.csv在您的系统上可用,并且您已经安装了Tensorflow 2.4.1和其他依赖项

之后,您应该能够更改在数据集上运行的代码s.t。目前,不考虑不平衡的数据。一旦您的模型开始预测更合理的内容,我们可以尝试通过使用一些平衡技术来改进它

weatherAUS.csv的代码 导入操作系统 将numpy作为np导入 作为pd进口熊猫 导入tensorflow作为tf 从sklearn.model\u选择导入列车\u测试\u拆分 从sklearn.preprocessing导入StandardScaler,OneHotEncoder 从tensorflow进口keras 从tensorflow.keras导入图层 def预处理DF_、拆分、num_管道、cat_管道、目标_编码器、数字_列、分类_列: 快速、肮脏和懒惰的预处理。 df_split=df_u.loc[分割] nums=num\u pipeline.transformdf\u split[数值列] cats=cat\u pipeline.transformdf\u split[categorical\u columns].dropcolumns='raintomory'.todense df\u num=pd.DataFramenums,index=df\u split[numeric\u columns]。index,columns=df\u split[numeric\u columns]。columns df_cat=pd.DataFramecats,index=df_split[categorical_columns]。index,columns=listnp.concatenatecat\u pipeline.categories_ df_X=df_num.joindf_cat.sort_索引 df_Y=targets_encoder.transformdf_split.rainmouther.values.reformate-1,1.todense df_Y=pd.DataFramedf_Y,index=df_split.index,columns=targets\u encoder.categories\u0]。排序\u索引 返回df_X,df_Y def样本发生器df\U X,df\U Y,nb\U天数:int,种子:int=None,最大样本:int=None: rnd=np.random.RandomStateseed 位置=df_X.index.get_level_values0 loc_索引=listrangelenlocations 计数=0 尽管如此: 如果最大样本数不是无且计数>=最大样本数: 打破 计数+=1 我们不能简单地任意抽样。需要确定 这些样本来自一个特定的城市。因此,我们先拍摄一个位置。。。 位置=位置[rnd.choiceloc_指数] .. 现在我们为那个位置选择了nb_日 offs=rnd.randint0,lendf_X.loc[位置]-nb_天 开始=结束 结束=休息日+非休息日 x=df_x.loc[location].iloc[start:end] y=df_y.loc[location].iloc[end-1] 产量x,y def fill_缺少_值DF_原始列、数字列、分类列: 快速且脏的缺失值预处理。 位置=setdf_raw.Location.unique 填充缺少的值 dfs=列表 对于位置中的位置: df_loc=df_raw[df_raw.Location==Location]。设置索引“日期” 我们重新采样和插值。。 df_num=df_loc[数值列]。重新采样'1D'。平均值。插值 .. 然后用平均值填充剩下的 df_num=df_num.fillnadf_num.mean df_cat=df_loc[categorical_columns.union{'Location'}] 对于分类功能,我们采用简单模式 df_cat=df_cat.fillnadf_cat.mode df_uu=df_unum.joindf_ucat df_uu=df_u.reset_index.set_index['Location','Date'] dfs.appenddf_ df_uu=pd.concatdfs df_uz=df_[~df_uz.raintomory.isnull] 仍然可能有一些缺少的值。同样,出于懒惰,我只是用平均值和模式填充nan值 df_num=df_u[numeric_columns].fillnadf_u[numeric_columns].mean 德福猫= df_u[分类_u列] df_cat=df_cat.fillnadf_cat.mode.iloc[0] df=df_num.joindf_cat df=df[sorteddf.columns] 返回df def主: 文件路径='weatherAUS.csv' assert os.path.existsfile_path,f'找不到数据文件:{file_path}' 种子=42 df_raw=pd.read_csvfile_路径 转换为日期时间 df_raw.Date=pd.to_datetimedf_raw.Date,推断_datetime_格式=True 如果一个列包含超过15%个空值,我们不认为它是特征。 我这样做只是出于懒惰。我们可能会做得更好。 零_阈值=0.15 null\u perc=df\u raw.isnull.sum/lendf\u raw 有用的列=setnull\u perc[null\u perc 从运算符导入itemgetter 进口ma tplotlib.pyplot作为plt 将numpy作为np导入 作为pd进口熊猫 导入seaborn作为sns 导入tensorflow作为tf 从sklearn.metrics导入混淆矩阵、分类报告 从sklearn.preprocessing导入StandardScaler,OneHotEncoder 从tensorflow进口keras 从tensorflow.keras导入图层 def预处理DF:pd.DataFrame,输入\u管道,目标\u编码器: 数值列=df。选择类型列=df['object'] inputs=inputs\u pipeline.transformdf[数值列] targets=targets\u encoder.transformdf.Rainfall 9am.values.reformate-1,1.todense df_inputs=pd.DataFrameinputs,index=df.index,columns=numeric_columns df\u targets=pd.DataFrametargets,index=df.index,columns=targets\u encoder.categories\u0] 返回df_输入,df_目标 def sample_generatordf_输入:pd.DataFrame,df_目标:pd.DataFrame,nb_天数:int,max_samples:int=None,seed=42: rnd=np.random.RandomStateseed 计数=0 尽管如此: 如果最大样本数不是无且计数>=最大样本数: 打破 计数+=1 offs=rnd.randint0,贷款输入-nb天 开始=结束 结束=休息日+非休息日 x=df_输入。iloc[开始:结束] y=df_targets.iloc[结束] 产量x,y def fill_缺少_值df:pd.DataFrame->pd.DataFrame: 非常懒惰和肮脏的预处理。用更复杂的东西代替这个。 df_targets=df['Rainfall上午9点]] df=df.重新采样'1D'。平均值.插值 df=df.fillnadf.mean df=df[sorteddf.columns] df=df.joindf_目标 df=df[~df.Rainfall9AM.isnull] df=df.dropna1 断言不为df.isnull.any.any df=df.sort_索引 返回df def get_modelnb_功能:int,nb_类:int: 输入=层。输入形状=无,nb_特征, x=输入 x=layers.Bidirectionallayers.LSTM128,返回顺序=True,辍学=0.1x x=layers.Bidirectionallayers.LSTM128,返回顺序=False,退出=0.1x x=layers.Densenb_类,activation='softmax'x 模型=keras。模型输入=输入,输出=x model.compile 优化器='adam', 损失class='classifical_crossentropy', “准确度” 回归模型 def主: fp='Arbaminch\u rainfaction\u classification.csv' df_raw=pd.read_csvfp 转换为日期时间 df_raw.Date=pd.to_datetime df_原始日期, 推断日期时间格式=真, 修复提供的数据包含错误无效日期,例如2009年2月29日 错误=‘强制’ df_raw=df_raw.set_索引'Date' 填充缺少的值 修正我预处理是懒惰的,应该改进! df=填充\缺失\值df\原始 创建列车/测试拆分 nb_列=intlendf*0.8 df\U系列数据,df\U测试数据=df[:nb\U系列],df[nb\U系列:] printf'Training samples:{lendf_train_data}' printf'测试样本:{lendf_Test_data}' 为数字和分类数据创建预处理管道 数值列=df列数据。选择类型列=['object']。列 输入\u管道=标准定标器 输入管道。fitdf列车数据[数字列] 目标\编码器=一个热编码器 targets\u encoder.fitdf\u train\u data.Rainfall 9am.dropna.unique.reforme-1,1 预处理 df_列_X,df_列_Y=预处理 df_列车数据, 输入管道=输入管道, 目标\编码器=目标\编码器 df_test_X,df_test_Y=预处理 df_测试_数据, 输入管道=输入管道, 目标\编码器=目标\编码器 断言lensetdf_train_X.index.get_level_values0.intersectionsetdf_test_X.index.get_level_values0==0 创建Tensorflow数据集 nb_天数=7天 批量大小=100 nb_features=lendf_列X列 nb_classes=lentargets_编码器。类别[0] 列车_数据=tf.data.Dataset.from_生成器 lambda:样本发生器F\U列X,df\U列Y,nb\U天=nb\U天, 输出形状=无,nb_特征,nb_类,, 输出类型=tf.float32,tf.float32 .预取批次大小。填充批次大小 test_data=tf.data.Dataset.from_生成器 lambda:样本发生器F_测试X,df_测试Y,nb_天数=nb_天数,最大样本数=1000, 输出形状=无,nb_特征,nb_类,, 输出类型=tf.float32,tf.float32 .预取批次大小。填充批次大小 获取模型 模型=获取模型nb\u要素=nb\u要素,nb\u类=nb\u类 开始训练 类别\u名称=列表目标\u编码器。类别\u0] 类别计数=dictdf\U列车数据。Rainfall 9AM。数值计数 总计=浮动列数据 class_weight=dict[i,np.logtotal/class_类名称中i,cname的计数[cname] 模型拟合 列车运行数据, 纪元=5, 每个历元的步数=100, 验证数据=测试数据。重复, 验证步骤=50, 等级重量=等级重量 评价模型 y_pred=np.argmaxmodel.predicttest_数据,轴=1 y_true=np.argmaxnp.concatenatel istmapitemgetter1,listtest_数据,轴=1 混淆=pd.dataframe混淆\u matrixy\u true,y\u pred,索引=类名称,列=类名称 plt.图 sns.heatmapMission,annot=True,fmt='d' 节目 printclassification\u reporty\u true,y\u pred,target\u names=类名 如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu': 主要的 精确回忆f1分数支持 大雨0.25 0.25 0.25 170 小雨0.120.31 0.17 126 中等0.18 0.37 0.24 140 无雨0.79 0.30 0.43 564 精度0.30 1000 宏平均值0.33 0.31 0.27 1000 加权平均值0.53 0.30 0.34 1000
这是太少的信息来帮助你。关于平衡:您可以应用欠排序过采样等方法。我也尝试了SMOTE和类权重技术来平衡类,但结果没有改变。如果您不发布代码和数据s.t,没有人可以帮助您。我们可以理解或重现您的问题。另外:不要发布截图。发布实际数据。我说您的帖子似乎包含的代码格式不正确。请使用“代码”工具栏按钮或CTRL+K键盘快捷键将所有代码缩进4个空格。有关更多编辑帮助,请单击[?]工具栏图标。当我复制代码时,我如何发布代码和数据?我不知道你在说什么。如果您不了解简单的降价是如何工作的,那么机器学习可能还为时过早。谢谢Stefen的帮助,但我的所有自变量都是数字的,只有Rainfall 9AM的目标变量是分类的,因此如果可能,请共享对不平衡的多类进行分类并适合我的数据的LSTM代码。谢谢。因此,我认为鸢尾花分类几乎与我的数据相似,这里是Iris kaggle.com/uciml/Iris的链接。或者我可以发送完整的数据集,如果你链接你的电子邮件,请,因为我不能在stackoverflow中发布。如果你看我的代码或运行它,你会看到df.rain明天也是一个分类变量。我的df.raintomory有两个类是和否。此外,数据集与您的数据集一样不平衡,完全符合您的要求。Iris flower数据集是不同的,因为它不包含时间序列,并且您不会在上面使用LSTM。你计算过每门课发生的次数吗?你不觉得奇怪吗,你的模型只预测中等?我认为不下雨是最常见的。再说一次:为什么要在create_数据集中调用标签上的stats.mode?我实现了上述s.t.示例。您可以运行并研究它。试着理解那里发生了什么,你应该能够更改代码s.t。它在你的数据集上工作。是的,我计算了发生的次数,你的假设是正确的,当我计算无雨2252次,大雨568次,Mdrate451次,小雨405次时,它从3937次缺失值中变成了3676次的总记录。我也不知道stats.mode是如何工作的,只是我从其他来源修改它来为LSTM创建3D,这就是为什么我需要建议我是正确的还是错误的,谢谢。我再次问你,我如何才能共享整个数据集。因为如果你看到数据集,你就能更了解问题。如果可能的话,请给我发电子邮件。