用经典的机器学习算法及深度学习模型进行文本情感分析(附数据集)

之前搞了很久的深度模型,看bert相关论文后感慨深度学习不单要有足够的理论知识(数学数学数学),也要有足够的资源(tpu、数据集、财力计算力)和足够的科学想象力(双向transfermer加mask就真的能搞定上下文信息?什么?位置?加个位置向量加成不就好了?),不禁为我这个通信半路出家的人感到凄凉。
相比之下其实许多亲民的基本算法模型已经能提供足够的精确度,而且在某些程度上机器学习算法的可解释性会更佳。
今天总结一下,当做是复习,并利用编程实现。

要总结的算法包括朴素贝叶斯、随机森林、支持向量机(svm)、逻辑回归、knn, 深度学习的就是简单的mlp、cnn 、gru、lstm

简单的都运行一遍比较一下效果
训练原料:六万多条电商评论,分为正负极性,平衡语料 ,利用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丰富,说明清晰,与keras都是我的心头好(说白了就是代码能力差的救星),只要单词认识就上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会更好。

SVM

支持向量机的核心就是所谓的那几个支持向量,软间隔和核函数。说来惭愧,之前还手动推过支持向量机,现在都忘得差不多了,都钻研深度学习模型去了。然而师兄和我说面试面试官都喜欢问支持向量机。因为里面知识点太多。准备找个时间好好复习再推一遍。model = SVC(kernel='linear')与前面一样只不过把model换了下而已。
精确度在***89%***左右出头,目前领先所有机器学习算法。

KNN

也没什么好讲了,世界上最好解释的机器学习算法(什么?朴素贝叶斯?我愣是给我师妹讲了半小时),戏称墙头草分类法,哪边人多就算哪边的。

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)

精度截图不见了,大概在七成,比贝叶斯好点还快,毕竟计算复杂度摆在那里。

然后就是深度模型了,首先来看看mlp(多层感知器)

也就是深度模型里最常说的全连接层,代码用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的精度,看得出来效果都比前面的好
但是看看图
用经典的机器学习算法及深度学习模型进行文本情感分析(附数据集)_第1张图片
明显在第五个的时候已经就可以停止迭代了,或者再调参(调参不可能的,这辈子不可能调参的)。
怎么说都是深度学习,而且维度还比较大,领先机器学习算法也比较正常(毕竟参数量摆在那),虽说不到一个百分点。

再看看测试用经典的机器学习算法及深度学习模型进行文本情感分析(附数据集)_第2张图片
虽然dropoout了还是赤裸裸的过拟合(算了就这样吧反正只是个输出层)

接下来就是cnn了

人工智能那么火,无非就是被这个玩意儿卷起来的,这里的卷积和本科的信号与系统不一样,刚学的时候还有点蒙
注意这里的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
用经典的机器学习算法及深度学习模型进行文本情感分析(附数据集)_第3张图片
参数就是那么多了,讲句来良心话,虽然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)

用经典的机器学习算法及深度学习模型进行文本情感分析(附数据集)_第4张图片
用经典的机器学习算法及深度学习模型进行文本情感分析(附数据集)_第5张图片
明显看得出来还有上升空间,测试精度还在往上走,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

深度学习阵营完胜

但我还是想给

逻辑回归

打call

感谢keras带给了这么简单的编程体验,封装程度可以真的说是傻瓜式的了。

最后就是这一年来对于深度学习学习的感想

好像很多师弟来问我深度学习究竟怎么样,应不应该转行(我是被迫的),在考察过整个人工智能热密度最高的深圳后,得到以下的想法以及认识(纯属个人):
1,兴趣是最好的老师,有热情的人才能创造价值。
2,有没有团队接纳你,有没有资源。(有专业的科研大牛(博士,精通行业趋势的道士)带,自己无疑也会更快进步。另外就是硬件条件,没有足够好足够多的gpu,确实很难去做深度做图像、视频、识别数据量大的工作,光模型参数都有1个g了,内存够吗?不过国内的服务器供应行业好像越来越多了,虽然收费也贵。)
3,数学基础好不好,凸(秃)优化了解一下?(我有个建筑设计的朋友跨考了经济系硕士,也要学习机器学习建模方法,叫苦连天)
4,工程能力强不强?(可以理解为代码能力,试想如果一个算法想到了段时间就可以实现并调试,看完了别人论文就可以复现出来,这种人也很适合机器学习岗位,毕竟都要通过代码来实现。我有个师兄就是代码能力超级强论文复现分分钟,虽然后面也只是去了中兴。在我接触那么多学习深度学习的人中,厉害的都是编程能力强的)

还有就是我感觉媒体把人工智能这个ip炒的太火,很多公众号进去把人工智能说的多好多好的到底还是卖课程。虽然人工智能的岗位薪资普遍偏高,尤其算法岗,但其实并不是谁都适合,而且竞争激烈,我们学校数理、机软、信工、甚至乎经管都有相应的机器学习课程,更别说一些院校还设置了专门的人工智能学院,也是为了那句中国要赢在人工智能的竞争上。有很多师兄也告诉过我工作不好找,只会python还是不够。
当然,其实说那么多,只要符合以上四个条件的任何一个都可以去尝试,这是一个入门容易(tensorflow都把库封装成那样了)但是做好很难的工作。但是阻止你进步的都只有你自己对不对?

ps:数据集地址

password: utqv

你可能感兴趣的:(用经典的机器学习算法及深度学习模型进行文本情感分析(附数据集))