机器学习平凡之路七

机器学习平凡之路七

  1. 循环神经网络
  2. 实例操作

循环神经网络

序列数据

序列数据,是特征的先后顺序对于数据的解释和处理十分重要的数据

语音数据,文本数据都是序列数据。比如一句话放在前面或者放在后面,会使文意有很大的不同

文本数据集的形状为3D张量(样本,序号,字编码)

时间序列数据,按时间顺序进行收集,用于描述现象随时间变化的情况,如果不记录时间戳,数字本身就没有意义

序列数据可以应用的场景:

  1. 文档分类,识别新闻的主题和书的类型,作者等
  2. 文档或时间序列对比,比如估测两个文档或两支股票的相关程度
  3. 文字情感分析,比如评论等情感划分为正面或者负面
  4. 时间序列预测,预测某地天气的历史数据来预测未来天气。
  5. 序列到序列的学习,比如两种语言之间的翻译

使用循环神经网络专门处理序列数据而生。它是一种具有记忆功能的神经网络,特点是能够把刚刚处理过的信息放进神经网络内存中。

原始文本如何转化为向量数据

通过One-hot编码分词

1
2
3
4
5
6
7
8
9
10
from keras.preprocessing.text import Tokenizer
words = ['laowang has a wechat account', 'he is no a nice persion','be careful.']
tokenizer = Tokenizer(num_words=30) # 词典大小设定为30个单词
tokenizer.fit_on_texts(words) # 根据句子编辑词典
sequences = tokenizer.texts_to_sequences(words) # 句子根据词典进行序号编码
ont_hot_matrix = tokenizer.texts_to_matrix(words,mode='binary') # 进行ont-hot编码
word_index = tokenizer.word_index
print(word_index)
print(sequences)
print(ont_hot_matrix)

image-20210401103103978

会存在一个问题本来2个单词,增加到了30维度。一般也就是常人说的维度灾难。解决这个问题,就是使用词嵌入。降低其维度,让本来的0,1本成包含一个意义的数字

实例操作

用Tokennizer进行分词

1
2
3
4
import pandas as pd 
import numpy as np
df_train = pd.read_csv('Reviews.csv')
df_train.head()

image-20210401104721082

1
2
3
4
5
6
7
8
9
10
11
from keras.preprocessing.text import Tokenizer
X_train_lst =df_train['Review Text'].fillna('').values
y_train = df_train['Rating'].values
dictionary_size = 20000
tokenizer = Tokenizer(num_words=dictionary_size)
tokenizer.fit_on_texts(X_train_lst)
X_train_tokenized_lst = tokenizer.texts_to_sequences(X_train_lst)
import matplotlib.pyplot as plt
word_per_comment = [len(comment) for comment in X_train_tokenized_lst]
plt.hist(word_per_comment,bins = np.arange(0,500,10))
plt.show()

image-20210401112743777

大部分的评论长度都在120以内

1
2
3
4
from keras.preprocessing.sequence import pad_sequences
max_comment_length = 120
X_train = pad_sequences(X_train_tokenized_lst, maxlen=max_comment_length)
print(X_train)

通过pad_sequences截取成相同的长度,长度大于120截断,小于120,填充无意义的0值

构建SimpleRNN

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.keras.models import Sequential
embedding_vecor_length = 60
rnn = tf.keras.Sequential()
rnn.add(layers.Embedding(dictionary_size,embedding_vecor_length,
input_length=max_comment_length))
rnn.add(layers.SimpleRNN(100))
rnn.add(layers.Dense(10,activation='relu'))
rnn.add(layers.Dense(6,activation='softmax'))
rnn.compile(loss='sparse_categorical_crossentropy',
optimizer='adam',
metrics=['acc'])
rnn.summary()

image-20210401114956156

1
2
3
4
history = rnn.fit(X_train,y_train,
validation_split=0.3,
epochs=10,
batch_size=64)

image-20210401115658875

可以参考的文章

1
2
y = rnn.predict(X_test[1])
rs = y.argmax(axis=1)[0]

预测结果代码使用

不同损失函数的区别

定义恒星是否有行星环绕

代码如下

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
49
50
from sklearn.utils import shuffle # 导入乱序工具
df_train = shuffle(df_train) # 乱序训练集
df_test = shuffle(df_test) # 乱序测试集


X_train = df_train.iloc[:, 1:].values # 构建特征集(训练)
y_train = df_train.iloc[:, 0].values # 构建标签集(训练)
X_test = df_test.iloc[:, 1:].values # 构建特征集(测试)
y_test = df_test.iloc[:, 0].values # 构建标签集(测试)
y_train = y_train - 1 # 标签转换成惯用的(0,1)分类
y_test = y_test - 1 # 标签转换成惯用的(0,1)分类
print (X_train) # 打印训练集中的特征
print (y_train) # 打印训练集中的标签

from keras.models import Sequential # 导入序贯模型
from keras import layers # 导入所有类型的层
from keras.optimizers import Adam # 导入优化器
model = Sequential() # 序贯模型
model.add(layers.Conv1D(32, kernel_size=10, strides=4,
input_shape=(3197, 1))) # 1D CNN层
model.add(layers.MaxPooling1D(pool_size=4, strides=2)) # 池化层
model.add(layers.GRU(256, return_sequences=True)) # 关键,GRU层够要大
model.add(layers.Flatten()) # 展平
model.add(layers.Dropout(0.5)) # Dropout层
model.add(layers.BatchNormalization()) # 批标准化
model.add(layers.Dense(1, activation='sigmoid')) # 分类输出层
opt = Adam(lr=0.0001, beta_1=0.9, beta_2=0.999, decay=0.01) # 设置优化器
model.compile(optimizer=opt, # 优化器
loss = 'binary_crossentropy', # 交叉熵
metrics=['accuracy']) # 准确率

history = model.fit(X_train,y_train, # 训练集
validation_split = 0.2, # 部分训练集数据拆分成验证集
batch_size = 128, # 批量大小
epochs = 4, # 训练轮次
shuffle = True) # 乱序


from sklearn.metrics import classification_report # 分类报告
from sklearn.metrics import confusion_matrix # 混淆矩阵
y_prob = model.predict(X_test) # 对测试集进行预测
y_pred = np.where(y_prob > 0.5, 1, 0) #将概率值转换成真值
cm = confusion_matrix(y_pred, y_test)
print('Confusion matrix:\n', cm, '\n')
print(classification_report(y_pred, y_test))

y_pred = np.where(y_prob > 0.15, 1, 0) # 进行阈值调整
cm = confusion_matrix(y_pred, y_test)
print('Confusion matrix:\n', cm, '\n')
print(classification_report(y_pred, y_test))

以上是CNN和RNN的组合

函数式API构建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from keras.optimizers import Adam # 导入Adam优化器
input = layers.Input(shape=(3197, 1)) # Input
# 通过函数式API构建模型
x = layers.Conv1D(32, kernel_size=10, strides=4)(input)
x = layers.MaxPooling1D(pool_size=4, strides=2)(x)
x = layers.GRU(256, return_sequences=True)(x)
x = layers.Flatten()(x)
x = layers.Dropout(0.5)(x)
x = layers.BatchNormalization()(x)
output = layers.Dense(1, activation='sigmoid')(x) # Output
model = Model(input, output)
model.summary() # 显示模型的输出
opt = Adam(lr=0.0001, beta_1=0.9, beta_2=0.999, decay=0.01) # 设置优化器
model.compile(optimizer=opt, # 优化器
loss = 'binary_crossentropy', # 交叉熵
metrics=['accuracy']) # 准确率

构建多头网络

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 构建正向网络
input_1 = layers.Input(shape=(3197, 1))
x = layers.GRU(32, return_sequences=True)(input_1)
x = layers.Flatten()(x)
x = layers.Dropout(0.5)(x)
# 构建逆向网络
input_2 = layers.Input(shape=(3197, 1))
y = layers.GRU(32, return_sequences=True)(input_2)
y = layers.Flatten()(y)
y = layers.Dropout(0.5)(y)
# 连接两个网络
z = layers.concatenate([x, y])
output = layers.Dense(1, activation='sigmoid')(z)
model = Model([input_1,input_2], output)
model.summary()

多输入输出参考文章

需要注意的点

  1. 数据集在升维之前,数据集进行逆序
1
X_train_rev = [X[::-1] for X in X_train] # 数据逆序之后再进行升阶
  1. 训练模型时同时指定正序和逆序数据集作为输入
1
2
3
4
5
6
model.fit([X_train, X_train_rev],y_train,
validation_split = 0.2
batch_size =128
epochs =1
shuffle =True
)