import os
import keras
import tensorflow as tf
from keras import layers
import numpy as np
#加载数据
def load_data(data_dir):
"""
data_dir:train的目录或test的目录
输出:
X:评论的字符串列表
y:标签列表(0,1)
"""
classes = ['pos', 'neg']
X, y = [], []
for idx, cls in enumerate(classes):
# 拼接某个类别的目录
cls_path = os.path.join(data_dir, cls)
os.listdir()
# os.listdir:函数只返回当前目录下的文件和文件夹名称,不包括子目录中的内容。
for file in os.listdir(cls_path):
# 拼接单个文件的目录(cls_path/file)
file_path = os.path.join(cls_path, file)
with open(file_path, encoding='utf-8') as f:
X.append(f.read().strip())
y.append(idx)
return X, np.array(y)
# input_size:输入样本的数量(词汇表大小)
# seq_len:表示序列的长度 (列的长度)
# embed_dim:表示词向量的维度(行的长度)
#Input Embedding和Positional Encoding
class PositionalEmbedding(layers.Layer): #PositionalEmbedding继承layers.Layer
def __init__(self, input_size, seq_len, embed_dim):
super(PositionalEmbedding, self).__init__()
self.seq_len = seq_len
# 词嵌入
self.tokens_embedding = layers.Embedding(input_size, embed_dim)
# 位置嵌入
self.positions_embedding = layers.Embedding(seq_len, embed_dim)
def call(self, inputs, *args, **kwargs):
# 生成位置id
positions = tf.range(0, self.seq_len, dtype='int32')
te = self.tokens_embedding(inputs)
pe = self.positions_embedding(positions)
return te + pe
# transformer-encoder
class TransformerEncoder(layers.Layer): #TransformerEncoder继承layers.Layer
def __init__(self, embed_dim, hidden_dim, num_heads, **kwargs):
super(TransformerEncoder, self).__init__(**kwargs)
# Multi-Head Attention层; num_heads 注意头的数量;key_dim 查询和键的每个注意力头的大小
self.attention = layers.MultiHeadAttention(
num_heads=num_heads, key_dim=embed_dim
)
# Feed Forward层
self.feed_forward = keras.Sequential([
layers.Dense(hidden_dim, activation='relu'), #隐藏层
layers.Dense(embed_dim)#输出层
])
# layernorm层
self.layernorm1 = layers.LayerNormalization()
self.layernorm2 = layers.LayerNormalization()
def call(self, inputs, *args, **kwargs):
# 计算Self-Attention
attention_output = self.attention(inputs, inputs)
# 进行第一个Layer & Norm
ff_input = self.layernorm1(inputs + attention_output)
# Feed Forward
ff_output = self.feed_forward(ff_input)
# 进行第二个Layer & Norm
outputs = self.layernorm2(ff_input + ff_output)
return outputs
def train():
# 超参数
vocab_size = 20000 #词汇表
seq_len = 180 #句子长度,超过会被截断
batch_size = 64 #最大批次
hidden_size = 1024 #全连接层维度
embed_dim = 256 #词向量维度(每一个词,我们需要用多少位的向量表示)
num_heads = 8 #多头8,也就是self-attion是8个
# 加载数据
X_train, y_train = load_data("C://Users//Administrator//Desktop//重要办公//gan//gan//aclImdb//train")
X_test, y_test = load_data("C://Users//Administrator//Desktop//重要办公//gan//gan//aclImdb//test")
#向量化 (Tokenizer分词算法是NLP大模型最基础的组件,基于Tokenizer可以将文本转换成独立的token列表,进而转换成输入的向量成为计算机可以理解的输入形式)
vectorization = layers.TextVectorization(
max_tokens=vocab_size, #词表大小
output_sequence_length=seq_len, #最大长度
pad_to_max_tokens=True
)
# 构建词表
vectorization.adapt(X_train)
# tokenize
X_train = vectorization(X_train)
X_test = vectorization(X_test)
# 构建模型
inputs = layers.Input(shape=(seq_len,)) #输入是180个词
# 180*20000||20000*256 == 180*256 的数据形状
x = PositionalEmbedding(vocab_size, seq_len, embed_dim)(inputs) #输入180个词,并且限制最大输入也就是180,超过就截切成180,180不够直接padding
x = TransformerEncoder(embed_dim, hidden_size, num_heads)(x)
#对文本数据进行平均池化操作
x = layers.GlobalAveragePooling1D()(x)
x = layers.Dropout(0.5)(x)
outputs = layers.Dense(1, activation='sigmoid')(x)
#构建函数模型,只需要传入输入和输出
model = keras.Model(inputs, outputs)
model.summary()
# 训练
model.compile(loss='binary_crossentropy', metrics=['accuracy'])
model.fit(
X_train,
y_train,
epochs=20,
batch_size=batch_size,
validation_data=(X_test, y_test)
)
if __name__ == '__main__':
train()
'''
TextVectorization 层将分三个阶段处理文本:
首先,移除标点符号,并将输入内容转换成小写。
接下来,将文本分割成单个字符串词的列表。
最后,使用已知词汇表将字符串映射为数字输出。
我们在此处可以尝试一个简单的方法,即多热编码,只考虑评论中是否存在术语。
例如,假设层词汇表为 ['movie', 'good', 'bad'],而评论为 'This movie was bad.'。
我们会将其编码为 [1, 0, 1],其中存在 movie(第一个词汇)和 bad(最后一个词汇)。
'''
# 训练数据目录 下载地址:http://ai.stanford.edu/~amaas/data/sentiment/
# - aclImdb
# - test
# - neg
# - pos
# - labeledBow.feat
# - urls_neg.txt
# - urls_pos.txt
# - train
# - neg
# - pos
'''
这是一个电影影评数据集,neg中包含的评论是评分较低的评论,而pos中包含的是评分较高的评论。我们需要的数据分别是test里面的neg和pos,
以及train里面的neg和pos(neg表示negative,pos表示positive)。下面我们开始处理。
'''