【机器学习】python多种⽅法实验⽐较⽂本情感⼆分类
实验⽬的及要求
实验源于“2019⼤数据挑战赛-预选赛”。
本预选赛要求选⼿建⽴⽂本情感分类模型,选⼿⽤训练好的模型对测试集中的⽂本情感进⾏预测,判断其情感为「Negative」或者「Positive」。所提交的结果按照指定的评价指标使⽤在线评测数据进⾏评测,达到或超过规定的分数线即通过预选赛。
数据样本格式:
其中,训练集的样本规模为6328,测试集的样本规模为2712。为保证⽐赛结果的真实性。每周⼀中午12点,将替换为新的测试集数据,供队伍答题使⽤。选⼿提交结果的评估指标是AUC(Area Under Curve)
实验仪器设备
硬件配置:CORE i5 7thGen处理器,8G内存,1050显卡
软件配置:Anaconda python3 Spyder
数据集:train.csv、20190506_test.csv
实验内容
本实验是⼀个典型的⼆分类问题,所有⽤于解决分类问题的模型如罗吉斯特回归、⽀持向量机、神经⽹络、朴素贝叶斯等都能适⽤。 其次,本实验还属于NLP中情感分析问题,处理的数据是印度尼西亚语的评论。情感分析⼀般有两种⽅法,⼀种是带词典的,⼀种是不带词典的。由于印尼语是⼩语种,词典难以寻,所以采⽤不带词典的⽅法。
(⼀)特征表⽰
我们需要分类的⽬标是句⼦,换句话说,句⼦是模型的输⼊。如何表⽰句⼦是解决该问题的关键。
1.One-Hot编码
one-hot向量将类别变量转换为机器学习算法易于利⽤的⼀种形式的过程,这个向量的表⽰为⼀项属性的特征向量,也就是同⼀时间只有⼀个激活点(不为0),这个向量只有⼀个特征是不为0的,其他都是0,特别稀疏。
句⼦可由每个词的one-hot向量相加得到。
2.word2vec
Word2vec 可以根据给定的语料库,通过优化后的训练模型快速有效地将⼀个词语表达成向量形式,为⾃然语⾔处理领域的应⽤研究提供了新的⼯具。Word2vec依赖skip-grams或连续词袋(CBOW)来建⽴神经词嵌⼊。
句⼦可由每个词的词向量直接相加或拼接成⼤的矩阵来表⽰。
3.doc2vec
Doc2Vec 或者叫做 paragraph2vec, sentence embeddings,是⼀种⾮监督式算法,可以获得
sentences/paragraphs/documents 的向量表达,是 word2vec 的拓展。
该⽅法可以直接得到句⼦的表⽰,表⽰形式为向量。
4.tf-idf
TF-IDF(term frequency–inverse document frequency,词频-逆向⽂件频率)是⼀种⽤于信息检索
(information retrieval)与⽂本挖掘(text mining)的常⽤加权技术。
TF-IDF是⼀种统计⽅法,⽤以评估⼀字词对于⼀个⽂件集或⼀个语料库中的其中⼀份⽂件的重要程度。字词的重要性随着它在⽂件中出现的次数成正⽐增加,但同时会随着它在语料库中出现的频率成反⽐下降。
TF-IDF的主要思想是:如果某个单词在⼀篇⽂章中出现的频率TF⾼,并且在其他⽂章中很少出现,则认为此词或者短语具有很好的类别区分能⼒,适合⽤来分类。
句⼦可由One-Hot思想,⽤每个单词的tf-idf值代替“1”来表⽰。
(⼆)模型训练
1. 朴素贝叶斯
朴素贝叶斯分类是⼀种⼗分简单的分类算法,叫它朴素贝叶斯分类是因为这种⽅法的思想真的很朴素,朴素贝叶斯的思想基础是这样的:对于给出的待分类项,求解在此项出现的条件下各个类别出现的概率,哪个最⼤,就认为此待分类项属于哪个类别。
2. 罗吉斯特回归
Logistic Regression是⽬前应⽤⽐较⼴泛的⼀种优化算法,利⽤logistic regression进⾏分类的只要思想是:根据现有数据对分类边界线建⽴回归公式,以此进⾏分类。“回归”⼀词源于最佳拟合,表⽰要到最佳拟合参数集。
Logistic回归的因变量可以是⼆分类的,也可以是多分类的,但是⼆分类的更为常⽤,也更加容易解释。所以实际中最常⽤的就是⼆分类的Logistic回归。
3. K近邻
K最近邻(k-Nearest Neighbor,KNN)分类算法,是⼀个理论上⽐较成熟的⽅法,也是最简单的机器学习算法之⼀。该⽅法的思路是:如果⼀个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的⼤多数属于某⼀个类别,则该样本也属于这个类别。
4. ⽀持向量机
⽀持向量机(support vector machines)是⼀种⼆分类模型,它的⽬的是寻⼀个超平⾯来对样本进⾏分割,分割的原则是间隔最⼤化,最终转化为⼀个凸⼆次规划问题来求解。
5. 随机森林
随机森林,指的是利⽤多棵树对样本进⾏训练并预测的⼀种分类器。该分类器最早由Leo Breiman和Adele Cutler提出,并被注册成了商标。简单来说,随机森林就是由多棵CART(Classification And Regression Tree)构成的。对于每棵树,它们使⽤的训练集是从总的训练集中有放回采样出来的,这意味着,总的训练集中的有些样本可能多次出现在⼀棵树的训练集中,也可能从未出现在⼀棵树的训练集中。
6. 前馈神经⽹络
前馈神经⽹络(Feedforward Neural Network),简称前馈⽹络,是⼈⼯神经⽹络的⼀种。在此种神经⽹络中,各神经元从输⼊层开始,接收前⼀级输⼊,并输出到下⼀级,直⾄输出层。整个⽹络中⽆反馈,可⽤⼀个有向⽆环图表⽰。
(三)模型⾃评
官⽹给的标准是计算AUC,由于我们只有训练集的Label数据,⽽官⽹提交次数有限,因此可计算训练集的AUC作为参考再进⾏提交。 AUC(Area Under Curve)被定义为ROC曲线下与坐标轴围成的⾯积,显然这个⾯积的数值不会⼤于1。⼜由于ROC曲线⼀般都处于y=x这条直线的上⽅,所以AUC的取值范围在0.5和1之间。使⽤AUC值作为评价标准是因为很多时候ROC曲线并不能清晰的说明哪个分类器的效果更好,⽽作为⼀个数值,对应AUC更⼤的分类器效果更好。
实验实施步骤
(⼀)特征提取
1.分词
句⼦由词组成,不管是为了得到one-hot、词向量、还是其他,处理的基本单元都是单词。⽽因为是外⽂单词,采⽤NLTK的分词可以帮助我们有效分词。
NLTK是⼀个⾼效的Python构建的平台,⽤来处理⼈类⾃然语⾔数据。它提供了易于使⽤的接⼝,通过这些接⼝可以访问超过50个语料库和词汇资源(如WordNet),还有⼀套⽤于分类、标记化、词⼲标记、解析和语义推理的⽂本处理库,以及⼯业级NLP库的封装器和⼀个活跃的讨论论坛。
rev_cut():对评论进⾏分词,分好的词放在“”中。
def rev_cut(review):
cut_file ='./'
with open(cut_file,'w', encoding='utf-8')as f:
for rev in review:
rev_cut =" ".join(nltk.word_tokenize(rev))#对句⼦进⾏分词
f.writelines(rev_cut +'\n')
return cut_file
2.word2vec得到句向量
Gensim是⼀款开源的第三⽅Python⼯具包,⽤于从原始的⾮结构化的⽂本中,⽆监督地学习到⽂本隐层的主题向量表达。 它⽀持包括TF-IDF,LSA,LDA,和word2vec在内的多种主题模型算法。
word_vec(cut_file,model_name,dimension):由分好的词训练得到每个单词的词向量,并保存。
get_sents_word_vec (review,dimension,model_name):载⼊训练好得模型,由词向量的平均值得到句向量。
def word_vec(cut_file,model_name,dimension):
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)
sentences = dels.word2vec.LineSentence(cut_file)#单词隔开,换⾏符换⾏
model = dels.word2vec.Word2Vec(sentences, hs=1,min_count=1,window=3,size=dimension)
model.save(model_name)
def get_sents_word_vec(review,dimension,model_name):
model = dels.word2vec.Word2Vec.load(model_name)
sent_vec = np.array([0]*dimension)
sents_vec =[]
for rev in review:
i =0
for word in nltk.word_tokenize(rev):
sent_vec = sent_vec + model[word]
i = i +1
sent_vec = sent_vec /float(i)
sents_vec.append(sent_vec)
sents_vec = np.array(sents_vec)
return sents_vec
3.doc2vec得到句向量
同样⽤Gensim库,可得到句向量,具体如下。
sent_vec(cut_file,model_name,dimension):由分好的词训练得到句⼦向量并将模型保存。
get_sents_sent_vec (review,dimension,model_name):载⼊模型,获取每条评论对应得句向量。
4.tf-idf及one-hot
Sklearn库提供了⽐较⽅便和⼀体的机器学习⽅法和tf-idf值计算,NLTK得到one-hot编码,这两部分相较⽐较简单,和具体的⽅法写在了⼀起,详情可见第(⼆)点模型训练。
(⼆)模型训练
1.机器学习⽅法
Sklearn库封装了⼤量机器学习的算法,对于应⽤只需调⽤,⽆需了解深度的细节。由于很多⽅法代码基本⼀致,因此在这⾥以结果最好的tf-idf和朴素贝叶斯的组合。
def train_NB_tfidf_skl(train_data,test_rev,all_rev):
labels = train_data['label']
train_rev = train_data['review']
ID = test_data['ID']
train_lab = get_lab(labels)
labs = train_lab
corpus = all_rev
vectorizer=CountVectorizer()
transformer = TfidfTransformer()
tfidf = transformer.fit_transform(vectorizer.fit_transform(corpus))
train_X = tfidf[0:len(train_rev)]
test_X = tfidf[len(train_rev):len(train_rev)+len(test_rev)]
#使⽤朴素贝叶斯进⾏训练
mnb = MultinomialNB(alpha=0.5, class_prior=None, fit_prior=True)
mnb.fit(train_X,labs)# 利⽤训练数据对模型参数进⾏估计
train_score =[pred[1]for pred in mnb.predict_proba(train_X)]
test_score =[pred[1]for pred in mnb.predict_proba(test_X)]
print('The Accuracy of Naive Bayes Classifier is:', mnb.score(train_X,labs))
train_score = np.array(train_score,dtype="float32")
test_score = np.array(test_score,dtype="float32")
print("AUC: ",cal_auc(train_score,train_lab))
result = pd.DataFrame({'ID':ID.T,'Pred':test_score})
<_csv("./result.csv",index =None)
2. 神经⽹络⽅法
使⽤tensorflow底层代码⾃⾏搭建前馈神经⽹络,分为输⼊层、隐层、输出层。其中隐层共有两层,每层有25个结点。输⼊层结点数与评论数对应,输出层与分类数对应,由于⼆分类,因此输出层可以只有⼀个结点。⽤预测感情得分与真实分值的均⽅误差作为损失函数,⽤⾃适应梯度下降算法求解,⽤Relu做激活函数。
def train_simplenn(train_data,sents_vec,test_sentsvec):
labels = train_data['label']
lab = get_lab(labels)
dataset_size = sents_vec.shape[0]#样本数
dimension = sents_vec.shape[1]#输⼊特征维度(特征个数)
emb_unit_counts_1 =25#隐层结点数
emb_unit_counts_2 =25#隐层结点数
batch_size =16#定义训练数据batch的⼤⼩
writelines使用方法pythonw1 = tf.Variable(tf.random_normal([dimension,emb_unit_counts_1],stddev=1,seed=1))
w2 = tf.Variable(tf.random_normal([emb_unit_counts_1,emb_unit_counts_2],stddev=1,seed=2))#初试化权重矩阵(⽣成相应⼤⼩的两个随机正态分布,均值为0,⽅差为1)
w3 = tf.Variable(tf.random_normal([emb_unit_counts_2,1],stddev=1,seed=3))
x = tf.placeholder(tf.float32,shape=(None,dimension),name='x-input')#在第⼀个维度上利⽤None,可以⽅便使⽤不⼤的batch⼤⼩
y_ = tf.placeholder(tf.float32,shape=(None,1),name='y-input')#在第⼀个维度上利⽤None,可以⽅便使⽤不⼤的batch⼤⼩
b1 = tf.s([emb_unit_counts_1]))# 隐藏层的偏向bias
b2 = tf.s([emb_unit_counts_2]))# 隐藏层的偏向bias
b3 = tf.s([1]))# 输出层的偏向bias
learning_rate =0.0015
#learning_rate = 0.01
#定义前向传播过程
a1 = tf.nn.softplus(tf.add(tf.matmul(x,w1),b1))#计算隐层的值
a2 = tf.nn.softplus(tf.add(tf.matmul(a1,w2),b2))#计算隐层的值
y = tf.nn.sigmoid(tf.add(tf.matmul(a2,w3),b3))#计算输出值
#定义损失函数和反向传播算法
loss = tf.reduce_mean(tf.square(y_-y))
#loss = -tf.reduce_mean(y_*tf.log(tf.clip_by_value(y,1e-10,1.0)))#cross_entropy
#learning_rate = 0.001 #ponential_dacay(0.1 ,STEPS,dataset_size/batch_size , 0.96 , staircase = True)
#optimizer = tf.train.MomentumOptimizer(learning_rate,momentum).minimize(loss) #在此神经⽹络中只有基础学习率的设置,没有指数衰减率,也没有正则项和滑动平均模型。
optimizer = tf.train.AdamOptimizer(learning_rate).minimize(loss)#在此神经⽹络中只有基础学习率的设置,没有指数衰减率,也没有正则项和滑动平均模型。
X = sents_vec
Y = lab
#以下创建⼀个session会话来运⾏TensorFlow程序
with tf.Session()as sess:
init_op = tf.initialize_all_variables()
sess.run(init_op)#在此利⽤以上两⾏对其中涉及的参数进⾏统⼀初始化
STEPS =100000#设定训练的轮数
for i in range(STEPS):
start =(i*batch_size)%dataset_size
end =min(start+batch_size,dataset_size)#每次选取batch_size个样本进⾏训练
sess.run(optimizer,feed_dict ={x:X[start:end],y_:Y[start:end]})#通过选取的样本训练神经⽹络并更新其中的参数
if i%1000==0:
score,loss_value = sess.run([y,loss],feed_dict={x:X,y_:Y})
print("After%dtraining step(s),loss on all data is%g"%(i,loss_value))
ain.Saver(max_to_keep=1)
saver.save(sess,'ckpt/emtion')#,global_step=i+1
print("AUC: ",cal_auc(score,lab))
X2 = test_sentsvec
test_pred = sess.run(y,feed_dict ={x:X2})
ID =[]
for s in range(1,len(X2)+1):
ID.append(s)
ID = np.array(ID)
result = pd.DataFrame({'ID':ID.T,'Pred':test_pred.T[0]})
<_csv("./result.csv",index =None)
(三)模型⾃评AUC的计算
AUC计算需先构造混淆矩阵,再画出ROC曲线,最后利⽤定积分⽅法计算⾯积得到。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论