天天看点

NLP-使用CNN进行文本分类

  • CNN最初用于处理图像问题,但是在自然语言处理中,使用CNN进行文本分类也可以取得不错的效果。
  • 在文本中,每个词都可以用一个行向量表示,一句话就可以用一个矩阵来表示,那么处理文本就与处理图像是类似的了。

目录

    • 目录
    • 一、卷积神经网络CNN
      • 1.模型说明
      • 2.卷积核
      • 3.CNN4Text
      • 4.两种参数调整问题
    • 二、使用实例:word2vec+CNN进行文本分类
      • 1.题目
      • 2.数据说明
      • 3.数据预处理
      • 4.训练NLP模型
      • 5.使用CNN
    • 三、扩展

一、卷积神经网络CNN

NLP-使用CNN进行文本分类

图片来源:Convolutional Neural Networks for Sentence Classification

1.模型说明

  • 在图像处理中使用不同的滤镜可以使图像凸显出不同的特征,我们把新的图像称为卷积后的特征图(第二层)。
  • 将特征图通过pooling层(第三层),这与人眼在看东西的时候会首先看到耀眼的内容一样,通常选取最大值或使用平均值来得到更小的特征图,这有助于计算。
  • 加上任意分类器,如SVM,LSTM等。

2.卷积核

  • 上述所说的滤镜(filter)就是我们的CNN模型中的卷积核,通过使用卷积核可以将我们的原始矩阵(CNN的第一层)变成CNN的第二层。
  • 卷积核是通过学习得到的,不能人工设置,这是很重要的一点,而刚开始的时候卷积核的设置是随机的。

3.CNN4Text

上述模型中是将每个单词作为一个特征向量,使用了二维和三维的卷积核进行Filter,我们也可以使用一整个句子作为特征向量,用一维的Filter进行扫描(1xN),如下图所示:

NLP-使用CNN进行文本分类

图片来源:七月在线视频课件(包括下图,原始来源我母鸡的了)

4.两种参数调整问题

  • 边界处理:Narrow 和Wide
NLP-使用CNN进行文本分类
  • 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语句后,来提高预测结果。

继续阅读