起因
有个研究生小姐姐需要我帮她做一个用神经网络对评论数据进行分类的模型,要用lstm算法,我当然是爽快的答应了。
拿到了数据集,看了一下,是这个样子(数据集不方便泄露,打码了)
然后小姐姐说只要先对一列标签分类就好,可以三个标签分开分类
开始解决问题
1.数据处理
首先嘛既然是要分类就要最先想想输入输出,输出肯定是三种不同的标签,比如对A标签,就是输出A0,A1,A2。那输入是什么呢?用一段中文输入?那计算机也不能理解啊,所以要把中文转换成数字才能让计算机“看得懂”,基本步骤就是对评论文本去除标点特殊符号等—>分词—>变为词向量。然后对标签重新定义,标为0,1,2,这样数据的预处理就基本结束了。(因为这个文本是小姐姐之前已经处理过一次的,没有什么缺失值和重复值,所以还是比较好的,但是有个大问题在后面再说)
#定义删除除字母,数字,汉字以外的所有符号的函数
def remove_punctuation(line):
line = str(line)
if line.strip()=='':
return ''
rule = re.compile(u"[^a-zA-Z0-9\u4E00-\u9FA5]")
line = rule.sub('',line)
return line
def stopwordslist(filepath):
stopwords = [line.strip() for line in open(filepath, 'r', encoding='utf-8').readlines()]
return stopwords
#加载停用词
stopwords = stopwordslist("./chineseStopWords.txt")
#删除除字母,数字,汉字以外的所有符号
data['clean_comment'] = data['comment'].apply(remove_punctuation)
#分词,并过滤停用词
data['cut_comment'] = data['clean_comment'].apply(lambda x: " ".join([w for w in list(jb.cut(x)) if w not in stopwords]))
data.head(10)
最后cut_comment就是分割好的数据了
2.训练集测试集的划分
把划分好的词语转成词向量并划分训练集和测试集
tokenizer = Tokenizer(num_words=MAX_NB_WORDS, filters='!"#$%&()*+,-./:;<=>?@[\]^_`{|}~', lower=True)
tokenizer.fit_on_texts(data['cut_comment'].values)
X = tokenizer.texts_to_sequences(data['cut_comment'].values)
#填充X,让X的各个列的长度统一
X = pad_sequences(X, maxlen=MAX_SEQUENCE_LENGTH)
#多类标签的onehot展开
Y = pd.get_dummies(data['label']).values
#拆分训练集和测试集
X_train, X_test, Y_train, Y_test = train_test_split(X,Y, test_size = 0.3, random_state = 42)
print(X_train)
print(X_train.shape,Y_train.shape)
print(X_test.shape,Y_test.shape)
3.定义分类模型
既然是分类那就要用激活函数,之前介绍的sigmoid函数对二分类效果很好,对多分类的问题还是需要用到另外一种激活函数-----softmax
这个用于三分类的例子就很好的展示了softmax在多分类问题上的好处。至于后面的损失函数等一系列的数学推导过程可以去看看那些大佬写的文章,他们讲的比我好的太多,然后LSTM的实现其实就是门的应用,例如下图
sigmoid可以让信息选择性的通过,而tanh就可以生成一个向量备选是否更新。其中最关键的遗忘门原理我还找到了一个图来演示
经过sigmoid函数,趋于0的信息丢弃,保留接近1的信息
model = Sequential()
model.add(Embedding(MAX_NB_WORDS, EMBEDDING_DIM, input_length=X.shape[1]))
model.add(SpatialDropout1D(0.2))
model.add(LSTM(50, dropout=0.6, recurrent_dropout=0.5))
model.add(Dense(3, activation='softmax'))#预测结果为3类
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
epochs = 20
batch_size = 64
history = model.fit(X_train, Y_train, epochs=epochs, batch_size=batch_size,validation_split=0.3,
callbacks=[EarlyStopping(monitor='val_loss', patience=3, min_delta=0.0001)])
accr = model.evaluate(X_test,Y_test)
print('测试集\n Loss: {:0.3f}\n Accuracy: {:0.3f}'.format(accr[0],accr[1]))
这个是对A标签的分类结果,最后测试集上的正确率只有65.4%
4.预测
def predict(text):
txt = remove_punctuation(text)
txt = [" ".join([w for w in list(jb.cut(txt)) if w not in stopwords])]
seq = tokenizer.texts_to_sequences(txt)
padded = pad_sequences(seq, maxlen=MAX_SEQUENCE_LENGTH)
pred = model.predict(padded)
pred_label= pred.argmax(axis=1)[0]
return label_df[label_df.label==pred_label]['lableA'].values[0]
pred_test=['第二,作者研究中并没有提出重要和经典的研究,也无法看出文献','第三,作者遗漏了最新的研究成果','第四,作者有抄袭的嫌疑,有一些地方可能需要再次确认','第一,作者没有对比不同的文献的差异性和优缺点']
pred_label=[]
for i in pred_test:
pred_label.append(predict(i))
print("预测的分类标签为:",pred_label)
5.原因
我换了对M的分类效果,测试集上的准确率有82.8%,这才像样嘛。
结束
人们一直都说程序猿不懂浪漫,直男活该单身什么的,但是我们有技术啊。做个调查对女孩子说话背后的心理贴上标签,训练个模型出来,那以后女孩子说什么就知道她是不是真的生气了。再也不会有“我真的没生气,你去陪你朋友们玩吧”然后就真的傻傻的陪朋友去了这样的情况发生。哈哈哈,这些当然只是调侃,毕竟是人之间的交往,机器是不能完全替代的,不然你的女朋友可能喜欢的是你的电脑而不是你了。、
真正的一个想法: 有的时候一小包餐巾纸没用完,只剩一张的时候你是怎么处理的呢?我多半时候是放在桌上然后就掉地上去,结果就直接当垃圾浪费了,又或者是明明不需要用纸的时候故意找点事把这张纸想办法用掉,这样还是很浪费,而且对于没有纸的空塑料袋的处理也很麻烦。我在想为什么不把这层塑料袋换成可以重复性使用的材质,就像中性笔只用换替芯而不用每次都去买一支新的笔,中国越来越重视环境,为什么不从这些生活中的小事开始着手改变呢。等有空了去设计个轻便方便携带,又易处理的餐巾纸盒。