当前位置:   article > 正文

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

机器学习 深度学习 文本情感分析

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

要总结的算法包括朴素贝叶斯、随机森林、支持向量机(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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

由此构建好了数据集划分
首先是

逻辑回归

逻辑回归,本质就是在线性回归的基础上加入了激活函数,使得输出可以用类似概率的形式输出来判定类别,而线性回归里的最小二乘法基本上可以说是机器学习中的经典。利用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)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在这里插入图片描述
比预料差太多了一样,思考了一下,会不会是因为假设特征间与特征间的独立性有问题?但无论如何这个效果也太差,五成还不如蒙?但是在垃圾邮件分类仍然是主流算法,速度比较快。

随机森林

这是我当时和老师第一次汇报内容就是决策树,所谓决策树就是不断去切分每个特征去形成节点。切分顺序可以根据切分前后的信息熵增益(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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

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

然后就是深度模型了,首先来看看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')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

在这里插入图片描述
最后一个epoch的精度,看得出来效果都比前面的好
但是看看图
在这里插入图片描述
明显在第五个的时候已经就可以停止迭代了,或者再调参(调参不可能的,这辈子不可能调参的)。
怎么说都是深度学习,而且维度还比较大,领先机器学习算法也比较正常(毕竟参数量摆在那),虽说不到一个百分点。

再看看测试在这里插入图片描述
虽然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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

之前犯了个错误,没有把各个模型的训练时间作对比(毕竟比 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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

在这里插入图片描述
在这里插入图片描述
明显看得出来还有上升空间,测试精度还在往上走,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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

看得出来还有很多烂七八糟的模型(尤其是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都是封装程度很高的模块,基本看着官方指南就可以上手,适合新人。

ps:数据集地址

password: utqv

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/679303
推荐阅读
相关标签
  

闽ICP备14008679号