- CNN最初用于处理图像问题,但是在自然语言处理中,使用CNN进行文本分类也可以取得不错的效果。
- 在文本中,每个词都可以用一个行向量表示,一句话就可以用一个矩阵来表示,那么处理文本就与处理图像是类似的了。
目录
-
- 目录
- 一、卷积神经网络CNN
- 1.模型说明
- 2.卷积核
- 3.CNN4Text
- 4.两种参数调整问题
- 二、使用实例:word2vec+CNN进行文本分类
- 1.题目
- 2.数据说明
- 3.数据预处理
- 4.训练NLP模型
- 5.使用CNN
- 三、扩展
一、卷积神经网络CNN
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIwczLcVmds92czlGZvwVP9EUTDZ0aRJkSwk0LcxGbpZ2LcBDM08CXlpXazRnbvZ2LcRlMMVDT2EWNvwFdu9mZvwVPjNjYzh3VhNTOxoVds12Y350MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2LcRHelR3LcJzLctmch1mclRXY39DMyIzMwMTNxITOxQDM4EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
图片来源:Convolutional Neural Networks for Sentence Classification
1.模型说明
- 在图像处理中使用不同的滤镜可以使图像凸显出不同的特征,我们把新的图像称为卷积后的特征图(第二层)。
- 将特征图通过pooling层(第三层),这与人眼在看东西的时候会首先看到耀眼的内容一样,通常选取最大值或使用平均值来得到更小的特征图,这有助于计算。
- 加上任意分类器,如SVM,LSTM等。
2.卷积核
- 上述所说的滤镜(filter)就是我们的CNN模型中的卷积核,通过使用卷积核可以将我们的原始矩阵(CNN的第一层)变成CNN的第二层。
- 卷积核是通过学习得到的,不能人工设置,这是很重要的一点,而刚开始的时候卷积核的设置是随机的。
3.CNN4Text
上述模型中是将每个单词作为一个特征向量,使用了二维和三维的卷积核进行Filter,我们也可以使用一整个句子作为特征向量,用一维的Filter进行扫描(1xN),如下图所示:
图片来源:七月在线视频课件(包括下图,原始来源我母鸡的了)
4.两种参数调整问题
- 边界处理:Narrow 和Wide
- Stride size:步伐大小
NLP-使用CNN进行文本分类
二、使用实例:word2vec+CNN进行文本分类
1.题目
用每日新闻预测金融市场变化
题目来源:Kaggle竞赛
代码作者:加号
2.数据说明
- Combined_News_DJIA.csv: 作者将数据combine成27列,第一列是日期,第二列是标签,其他25列是每日的前25条新闻,通过热门程度进行排序得来。
- 这是一个二分类问题,‘1’表示这一天的股票值上升或保持不变;‘0’表示下降。
- 训练集和测试集的比例是8:2
3.数据预处理
①导入所需要的库
import pandas as pd
import numpy as np
from sklearn.metrics import roc_auc_score
from datetime import date
②读入数据
可以通过
data.head()
查看数据的长相
③分割数据集
train = data[data['Date'] < '2015-01-01']
test = data[data['Date'] > '2014-12-31']
④处理数据集
X_train = train[train.columns[:]]
#去掉日期和标签,把每条新闻做成单独的句子,集合在一起
corpus = X_train.values.flatten().astype(str)
#获取语料库
X_train = X_train.values.astype(str)
X_train = np.array([' '.join(x) for x in X_train])
X_test = test[test.columns[:]]
X_test = X_test.values.astype(str)
X_test = np.array([' '.join(x) for x in X_test])
y_train = train['Label'].values
y_test = test['Label'].values
- 通过.columns[]获取特定列;
- flatten()函数将一个嵌套多层的数组array转化成只有一层的数组;
-
numpy array存储单一数据类型的多维数组;
说明:
corpus是全部我们『可见』的文本资料。假设每条新闻就是一句话,把他们全部flatten()就会得到list of sentences。
X_train和X_test不能随便flatten,他们需要与y_train和y_test对应。
⑤分词
from nltk.tokenize import word_tokenize
corpus = [word_tokenize(x) for x in corpus]
X_train = [word_tokenize(x) for x in X_train]
X_test = [word_tokenize(x) for x in X_test]
- corpus的分词结果里,第二维数据是一个个的句子,而X_train的第二维数据是一天中整个新闻的合集,对应每个label。
- 可以通过X_train[:2]和corpus[:2]查看数据的变化情况。
⑥预处理
- 小写化
- 删除停止词
- 删除数字和符号
-
lemma词性还原
把这些步骤统一合成一个func
# 停止词
from nltk.corpus import stopwords
stop = stopwords.words('english')
# 数字
import re
def hasNumbers(inputString):
return bool(re.search(r'\d', inputString))
# 正则表达式,出现数字的时候返回true
# 特殊符号
def isSymbol(inputString):
return bool(re.match(r'[^\w]', inputString))
"""
正则表达式,\w表示单词字符[A-Za-z0-9_]
[^\w]表示取反==\W非单词字符
全是特殊符号时返回true
"""
# lemma
from nltk.stem import WordNetLemmatizer
wordnet_lemmatizer = WordNetLemmatizer()
def check(word):
"""
如果需要这个单词,则True
如果应该去除,则False
"""
word= word.lower()
if word in stop:
return False
elif hasNumbers(word) or isSymbol(word):
return False
else:
return True
# 把上面的方法综合起来
def preprocessing(sen):
res = []
for word in sen:
if check(word):
# 这一段的用处仅仅是去除python里面byte存str时候留下的标识。。之前数据没处理好,其他case里不会有这个情况
word = word.lower().replace("b'", '').replace('b"', '').replace('"', '').replace("'", '')
res.append(wordnet_lemmatizer.lemmatize(word))
return res
把三组数据集通过上面的函数进行预处理:
corpus = [preprocessing(x) for x in corpus]
X_train = [preprocessing(x) for x in X_train]
X_test = [preprocessing(x) for x in X_test]
4.训练NLP模型
使用最简单的word2vec
from gensim.models.word2vec import Word2Vec
model = Word2Vec(corpus, size=, window=, min_count=, workers=)
Word2Vec:word to vector ,将单词转化为向量,这时候每个单词都可以像查字典一样读取坐标。
例如,使用
model['white']
就可以获得单词‘white’的w2v坐标。
5.使用CNN
①获取矩阵信息
- 用vector表示出一个大matrix,使用CNN做‘降维+注意力’
- 通过调大参数可以增加复杂度和准确度
- 确定padding size,使生成的matrix是一样的size
- 下面代码可以直接调用keras的sequence方法
# 说明,对于每天的新闻,我们会考虑前256个单词。不够的我们用[000000]补上
# vec_size 指的是我们本身vector的size
def transform_to_matrix(x, padding_size=, vec_size=):
res = []
for sen in x:
matrix = []
for i in range(padding_size):
try:
matrix.append(model[sen[i]].tolist())
except:
# 这里有两种except情况,
# 1. 这个单词找不到
# 2. sen没那么长
# 不管哪种情况,我们直接贴上全是0的vec
matrix.append([] * vec_size)
res.append(matrix)
return res
处理训练集/测试集
X_train = transform_to_matrix(X_train)
X_test = transform_to_matrix(X_test)
# print(X_train[1])
# 查看数据的长相
最后,我们得到的就是一个大大的Matrix,它的size是 128 * 256。每一个matrix对应一个数据点(标签)。
②reshape输入集
在进行下一步之前,需要将input 数据reshape一下,原因是要使每一个matrix的外部包裹一层维度,来告诉我们的CNN model,每个数据点都是独立的,没有前后关系。
(对于股票来说,存在前后关系,可以使用CNN+LSTM模型实现记忆功能)
# 搞成np的数组,便于处理
X_train = np.array(X_train)
X_test = np.array(X_test)
# 看看数组的大小
print(X_train.shape)
print(X_test.shape)
结果:
(1611, 256, 128)
(378, 256, 128)
X_train = X_train.reshape(X_train.shape[], , X_train.shape[], X_train.shape[])
X_test = X_test.reshape(X_test.shape[], , X_test.shape[], X_test.shape[])
#通过print(X_test)观察与前者的区别,就是多了一个括号
print(X_train.shape)
print(X_test.shape)
结果:
(1611, 1, 256, 128)
(378, 1, 256, 128)
③定义CNN模型
from keras.preprocessing import sequence
from keras.models import Sequential
from keras.layers import Convolution2D, MaxPooling2D
from keras.layers.core import Dense, Dropout, Activation, Flatten
# set parameters:
batch_size =
n_filter =
filter_length =
nb_epoch =
n_pool =
# 新建一个sequential的模型
model = Sequential()
model.add(Convolution2D(n_filter,filter_length,filter_length,
input_shape=(, , )))
model.add(Activation('relu'))
model.add(Convolution2D(n_filter,filter_length,filter_length))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(n_pool, n_pool)))
model.add(Dropout())
model.add(Flatten())
# 后面接上一个ANN
model.add(Dense())
model.add(Activation('relu'))
model.add(Dropout())
model.add(Dense())
model.add(Activation('softmax'))
# compile模型
model.compile(loss='mse',
optimizer='adadelta',
metrics=['accuracy'])
④获取结果
model.fit(X_train, y_train, batch_size=batch_size, nb_epoch=nb_epoch,
verbose=)
score = model.evaluate(X_test, y_test, verbose=)
print('Test score:', score[])
print('Test accuracy:', score[])
三、扩展
①对于CNN而言,无论input什么内容,只要符合规矩,都可以process,所以可以不使用word2vec。甚至可以使用ASCII码(0,256)来表达每个位置上的字符,然后组成一个大大的matrix。每个字符都可以被表示出来,而且使有意义的。
②分类器的使用也是多种多样的,可以用LSTM或者RNN等接在CNN的那句Flatten语句后,来提高预测结果。