赞
踩
搞了很久的深度模型,看了bert相关论文后感慨深度学习不单要有足够的理论知识(数学数学数学),也要有足够的资源(tpu、数据集、财力计算力)和足够的科学想象力(双向transfermer加mask就真的能搞定上下文信息?位置?加个位置向量加成不就好了?),不禁为自己的idea的匮乏感到凄凉。
相比之下其实许多亲民的基本算法模型已经能提供足够的精确度,而且在某些程度上机器学习算法的可解释性会更佳。
今天总结一下,当做是复盘,巩固一下机器学习的基础,并利用编程实现。
简单的都运行一遍比较一下效果
训练原料:六万多条电商评论,分为正负极性,平衡语料 ,利用64维训练好的word2vec映射词向量模型(1.4个g,当时训练了几天,虽然不久就会被bert模型代替了)
训练框架及环境:tensorflow-gpu 1.3 keras 2.0 sklearn gpu1080ti(亲测i5cpu笔记本也可以跑,但是得大幅减少数据量,除非内存很多很多,其次就是慢,指用tf、keras的cpu版)
必要的步骤,去停用词,设置timestep等(句子长度,我这里设词向量维度为维度)
至于数据预处理就比较繁杂,在这里就不说了。我一般习惯把不同极性分开存放便于提取,有人则喜欢放一起用正则式提取,但是对我而言代码永远是实现目标的工具,当然越少越好。
VECTOR_DIR = 'embedding_64.bin' # 词向量模型文件,同目录下 model = gensim.models.KeyedVectors.load_word2vec_format(VECTOR_DIR, binary=True) def rep_sentencevector(sentence): word_list = [word for word in sentence.split(' ')] embedding_dim = 64 embedding_matrix = np.zeros(embedding_dim) for index, word in enumerate(word_list): try: embedding_matrix += model[word] except: pass return embedding_matrix/len(word_list) def build_traindata(): X_train = list() Y_train = list() X_test = list() Y_test = list() for line in open('./data/train.txt',encoding="UTF-8"): line = line.strip().strip().split('\t') sent_vector = rep_sentencevector(line[-1]) X_train.append(sent_vector) if line[0] == '1': Y_train.append(1) else: Y_train.append(0) for line in open('./data/test.txt',encoding="UTF-8"): line = line.strip().strip().split('\t') sent_vector = rep_sentencevector(line[-1]) X_test.append(sent_vector) if line[0] == '1': Y_test.append(1) else: Y_test.append(0) return np.array(X_train), np.array(Y_train), np.array(X_test), np.array(Y_test)
由此构建好了数据集划分
首先是
逻辑回归,本质就是在线性回归的基础上加入了激活函数,使得输出可以用类似概率的形式输出来判定类别,而线性回归里的最小二乘法基本上可以说是机器学习中的经典。利用sklearn包倒入轻松实现,这里利用sklearn的acc评价函数来查看训练时候的概率值
def train_LogisticRegression(X_train, Y_train, X_test, Y_test):
model = LogisticRegression()
model.fit(X_train, Y_train)
joblib.dump(model, './model/LogisticRegression_model.m')
acc = accuracy_score(Y_test,model.predict(X_test))
print("lr精确度%f"%acc)
sklearn真的是开源界的暖男,api丰富,说明清晰,只要单词认识就上sk官网撸,简单粗暴。
可以看到测试精度到了可怕的88.9%,有句话说的好,分类任务都先用逻辑回归试一试,能用逻辑回归还用啥rnn?因为不涉及调参,所以也就没有验证集了,深度会用验证集但是也别指望细调,毕竟我写这篇记录的预算也只有一下午。
朴素贝叶斯是将已知的特征群去求状态的问题,根据贝叶斯公布方式巧妙的转换成已知状态求特征群的问题,然后通过假设特征间独立(朴素之来源)从而变成了独立特征与状态的概率乘积,简单,但精度较差。
def train_bayes(X_train, Y_train, X_test, Y_test):
model = GaussianNB()
model.fit(X_train, Y_train)
joblib.dump(model, './model/sentiment_bayes_model.m')
acc = accuracy_score(Y_test,model.predict(X_test))
print("bayes精确度%f"%acc)
比预料差太多了一样,思考了一下,会不会是因为假设特征间与特征间的独立性有问题?但无论如何这个效果也太差,五成还不如蒙?但是在垃圾邮件分类仍然是主流算法,速度比较快。
这是我当时和老师第一次汇报内容就是决策树,所谓决策树就是不断去切分每个特征去形成节点。切分顺序可以根据切分前后的信息熵增益(id3),改进后的根据信息熵增益率(C4.5),以及用来分割连续值的gini系数(CART)的决策树,而随机森林就是一堆决策树的集成,属于bagging,就是一堆一起来,每颗分配不同的数据或者维度然后平均每棵树输出的结果。
接近八成,效果中规中矩,可以看到效果居然还不上逻辑回归,就问问还有谁。
在许多文章里,svm和随机森林都被称为是机器学习里的万金油,啥都能干,能分类能回归,还能保证一定的精确度,但我隐约感觉svm会更好。
支持向量机的核心就是所谓的那几个支持向量,软间隔和核函数。说来惭愧,之前还手动推过支持向量机,现在都忘得差不多了,都钻研深度学习模型去了。然而师兄和我说面试面试官都喜欢问支持向量机。因为里面知识点太多。准备找个时间好好复习再推一遍。model = SVC(kernel='linear')
与前面一样只不过把model换了下而已。
精确度在***89%***左右出头,目前领先所有机器学习算法。
也没什么好讲了,世界上最好解释的机器学习算法,戏称墙头草分类法,哪边人多就算哪边的。
def train_knn(X_train, Y_train, X_test, Y_test): for x in range(1, 15): model = KNeighborsClassifier(n_neighbors=x) model.fit(X_train, Y_train) preds = knnclf.predict(X_test) num = 0 num = 0 preds = preds.tolist() for i, pred in enumerate(preds): if int(pred) == int(Y_test[i]): num += 1 print('K= ' + str(x) + ', precision_score:' + str(float(num) / len(preds))) #选择K=20进行KNN训练 model = KNeighborsClassifier(n_neighbors=14) model.fit(X_train, Y_train) joblib.dump(model, './model/sentiment_knn_model.m') acc = accuracy_score(Y_test,model.predict(X_test)) print("knn精确度%f"%acc)
精度截图不见了,大概在七成,比贝叶斯好点还快,毕竟计算复杂度摆在那里。
也就是深度模型里最常说的全连接层,代码用keras写的(import的)
keras支持可视化,我就简单的打印出了
def train_mlp(X_train, Y_train, X_test, Y_test): from keras.models import Sequential from keras.layers import Dense, Dropout model = Sequential() model.add(Dense(64, input_dim=(2560), activation='relu'))#2560=40*64,句子数乘以词向量维度 model.add(Dropout(0.5)) model.add(Dense(64, activation='relu')) model.add(Dropout(0.5)) model.add(Dense(2, activation='sigmoid')) model.compile(loss='binary_crossentropy', optimizer='rmsprop', metrics=['accuracy']) history=model.fit(X_train, Y_train, batch_size=100, epochs=20, validation_data=(X_test, Y_test)) # 绘制训练 & 验证的准确率值 plt.plot(history.history['acc']) plt.plot(history.history['val_acc']) plt.title('Model accuracy') plt.ylabel('Accuracy') plt.xlabel('Epoch') plt.legend(['Train', 'Test'], loc='upper left') plt.show() # 绘制训练 & 验证的损失值 plt.plot(history.history['loss']) plt.plot(history.history['val_loss']) plt.title('Model loss') plt.ylabel('Loss') plt.xlabel('Epoch') plt.legend(['Train', 'Test'], loc='upper left') plt.show() model.save('./model/sentiment_mlp_model.h5')
最后一个epoch的精度,看得出来效果都比前面的好
但是看看图
明显在第五个的时候已经就可以停止迭代了,或者再调参(调参不可能的,这辈子不可能调参的)。
怎么说都是深度学习,而且维度还比较大,领先机器学习算法也比较正常(毕竟参数量摆在那),虽说不到一个百分点。
再看看测试
虽然dropoout了还是赤裸裸的过拟合(算了就这样吧反正只是个输出层)
注意这里的CNN1D其实和图像里的CNN2D是一样的,只不CNN1D默认了数据的维度为词向量的维度而已.
def CNN1D_train(x_train,y_train,x_test,y_test,model_name=''): #y_test = utils.to_categorical(y_test) #y_train = utils.to_categorical(y_train) n_classes=y_train.shape[1] print(n_classes) input_vec = Input(shape=(40,64)) x = Conv1D(128, 3,use_bias=True, activation='relu')(input_vec) #x = MaxPooling1D(5)(x) x = Conv1D(64, 3, activation='relu')(x)#filter_size=3*64(紧跟上层的input的维度,自动的) #x = MaxPooling1D(5)(x) x=Dropout(0.5)(x) x = Conv1D(64, 3, activation='relu')(x) x = GlobalMaxPooling1D()(x) x = Dense(128, activation='relu')(x) output = Dense(n_classes, activation='sigmoid')(x) model = Model(inputs=input_vec, outputs=output) #model.compile(loss=lambda y_true,y_pred: y_true*K.relu(0.9-y_pred)**2 + 0.25*(1-y_true)*K.relu(y_pred-0.1)**2,optimizer='adam', metrics=['accuracy']) model.compile(loss='categorical_crossentropy',optimizer='adam',metrics=['accuracy',recall_threshold(0.5),precision_threshold(0.5)]) model.summary() # filepath = "model/CNN1D_best_whight.hdf5" # checkpoint = ModelCheckpoint(filepath, monitor='val_loss',verbose=1,save_best_only=True,save_weights_only=True, mode='auto') # callbacks_list = [checkpoint] starttime = datetime.datetime.now() #model.fit(x_train, y_train,batch_size=256,epochs=30,verbose=1,validation_data=(x_test, y_test),callbacks=callbacks_list) history =model.fit(x_train, y_train,batch_size=256,epochs=30,verbose=1,validation_data=(x_test, y_test)) # 绘制训练 & 验证的准确率值 plt.plot(history.history['acc']) plt.plot(history.history['val_acc']) plt.title('Model accuracy') plt.ylabel('Accuracy') plt.xlabel('Epoch') plt.legend(['Train', 'Test'], loc='upper left') plt.show() # 绘制训练 & 验证的损失值 plt.plot(history.history['loss']) plt.plot(history.history['val_loss']) plt.title('Model loss') plt.ylabel('Loss') plt.xlabel('Epoch') plt.legend(['Train', 'Test'], loc='upper left') plt.show() #score, acc,recall,precision= model.evaluate(x_test, y_test, batch_size=128) endtime = datetime.datetime.now() print("\nTraing using time:%.4f" % ((endtime - starttime).seconds)) print("\nTest score: %.4f, accuracy: %.4f, recall: %.4f,precision: %.4f s" % (score, acc,recall,precision)) print('&&end&&' * 10)
之前犯了个错误,没有把各个模型的训练时间作对比(毕竟比 performance比不过人家,只能比别的了,这个思想早该根深蒂固了,科研的苦TT)
cnn就是调参大户了,一般任务都是先选模型>层数>参数>参数>参数>参数>…>TT
参数就是那么多了,虽然cnn参数多,但是结构上是并行计算,速度还是很快的,而且准确率也够高,不会输给rnn结构太多。一般来说cnn的结构窄而深效果会好(没人知道为啥),CNN就是调参问题太多了。
至于gru/LSTM其实都不想怎么谈了,在新的模型不断迭代下(以注意力模型为首)克服了cnn的上下文关联问题及语义、rnn的训练慢、长文本效果的短板。这些性价比不高的模型很容易就会成为古董(科研上)。
下面我就直接po效果最好的BI-GRU的训练图以及所有效果排名了。关于深度学习-人工智能,其实只不过还是数据学习。
因为是在慢的忍无可忍,我把epoch数改为了3,实际上测试准确率最高可以到96.7%(测试集不是验证集),而在这个数据即跑的最好的是之前用的word2vec+textcnn+attention 最好performance到了98%,但是由于不是公开数据所以没什么说服力,只能自己比较了模型用了。
BI-GRU代码
def biGRU_train(x_train,y_train,x_test,y_test,model_name=''): #y_test0 = to_categorical(y_test) #y_train = to_categorical(y_train) num_classes=y_train.shape[1] input1=Input(shape=(40,64)) x=Bidirectional(GRU(128, return_sequences=True))(input1) x=Bidirectional(GRU(128,dropout=0.25, recurrent_dropout=0.25))(x) x=Dense(num_classes,activation='sigmoid')(x) model = Model(inputs=input1, outputs=x) #model.add(Activation('softmax')) #model.compile(loss='categorical_crossentropy', optimizer='rmsprop') model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy',recall_threshold(0.5),precision_threshold(0.5)]) model.summary() filepath="model/biGRU_best_whight.hdf5" checkpoint = ModelCheckpoint(filepath, monitor='val_loss',verbose=1,save_best_only=True,save_weights_only=True, mode='auto') callbacks_list = [checkpoint] #print('Train...') starttime = datetime.datetime.now() model.fit(x_train, y_train, batch_size=128, epochs=30, validation_data=(x_test, y_test), callbacks=callbacks_list) score, acc,recall,precision= model.evaluate(x_test, y_test, batch_size=128) endtime = datetime.datetime.now() print("\nTraing using time:%.4f" % ((endtime - starttime).seconds)) print("\nTest score: %.4f, accuracy: %.4f, recall: %.4f,precision: %.4f" % (score, acc,recall,precision)) print('&&end&&'*10)
明显看得出来还有上升空间,测试精度还在往上走,loss还在减少,两个epoch太少了。效果很好,但确实太慢了
这是这次参与测评的所有模型
#biLSTM_train(x_train,y_train,x_test,y_test,model_name='')
#LSTM_train(x_train,y_train,x_test,y_test,model_name='')
#biGRU_train(x_train,y_train,x_test,y_test,model_name='')
CNN1D_train(x_train,y_train,x_test,y_test,model_name='')
#GRU_train(x_train,y_train,x_test,y_test,model_name='')
#CNN_LSTM_train(x_train,y_train,x_test,y_test,model_name='')
#train_LogisticRegression(x_train, y_train, x_test, y_test)
#train_decisiontree(x_train, y_train, x_test, y_test)
# train_bayes(x_train, y_train, x_test, y_test)
# train_knn(x_train, y_train, x_test, y_test)
#train_mlp(x_train, y_train, x_test, y_test)
看得出来还有很多烂七八糟的模型(尤其是cnn-lstm模型,我下意识的觉得cnn的池化层会破坏语序信息牺牲了RNN的提取效果,同时lstm还会使整个模型训练变慢,这种模型不可取不可取)
排名
biGRU ——96.7—— 15个epoch
biLSTM—— 95.9—— 15个epoch
GRU ——95.4—— 15个epoch
LSTM ——95.2—— 15个epoch
CNN1D ——94.6—— 20个epoch
mlp ——90
SVM ——89.9
LogisticRegression ——88.9
Randomdecisiontree ——77.9
knn ——70.4
bayes ——54
但是逻辑回还是在二分类任务显示出了很好的性价比,可以作为baseline
sklearn和keras都是封装程度很高的模块,基本看着官方指南就可以上手,适合新人。
password: utqv
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。