NLP实践-数据集探索

  • 1、THUCNews
    1.1 数据集介绍
    本数据集是清华NLP组提供的THUCNews新闻文本分类数据集的一个子集(原始的数据集大约74万篇文档,训练起来需要花较长的时间)。
    本次训练使用了其中的10个分类(体育, 财经, 房产, 家居, 教育, 科技, 时尚, 时政, 游戏, 娱乐),每个分类6500条,总共65000条新闻数据。
    数据集划分如下:
    cnews.train.txt: 训练集(50000条)
    cnews.val.txt: 验证集(5000条)
    cnews.test.txt: 测试集(10000条)
    1.2 预处理
    代码中的函数说明如下:
    read_file(): 读取文件数据;
    build_vocab(): 构建词汇表,使用字符级的表示,这一函数会将词汇表存储下来,避免每一次重复处理;
    read_vocab(): 读取上一步存储的词汇表,转换为{词:id}表示;
    read_category(): 将分类目录固定,转换为{类别: id}表示;
    to_words(): 将一条由id表示的数据重新转换为文字;
    preocess_file(): 将数据集从文字转换为固定长度的id序列表示;
    batch_iter(): 为神经网络的训练准备经过shuffle的批次的数据。
# coding: utf-8 import sys from collections import Counter import numpy as np import tensorflow.contrib.keras as kr if sys.version_info[0] > 2: is_py3 = True else: reload(sys) sys.setdefaultencoding("utf-8") is_py3 = False def native_word(word, encoding='utf-8'): """如果在python2下面使用python3训练的模型,可考虑调用此函数转化一下字符编码""" if not is_py3: return word.encode(encoding) else: return word def native_content(content): if not is_py3: return content.decode('utf-8') else: return content def open_file(filename, mode='r'): """ 常用文件操作,可在python2和python3间切换. mode: 'r' or 'w' for read or write """ if is_py3: return open(filename, mode, encoding='utf-8', errors='ignore') else: return open(filename, mode) def read_file(filename): """读取文件数据""" contents, labels = [], [] with open_file(filename) as f: for line in f: try: label, content = line.strip().split('\t') if content: contents.append(list(native_content(content))) labels.append(native_content(label)) except: pass return contents, labels def build_vocab(train_dir, vocab_dir, vocab_size=5000): """根据训练集构建词汇表,存储""" data_train, _ = read_file(train_dir) all_data = https://www.it610.com/article/[] for content in data_train: all_data.extend(content) counter = Counter(all_data) count_pairs = counter.most_common(vocab_size - 1) words, _ = list(zip(*count_pairs)) # 添加一个 来将所有文本pad为同一长度 words = [''] + list(words) open_file(vocab_dir, mode='w').write('\n'.join(words) + '\n') def read_vocab(vocab_dir): """读取词汇表""" # words = open_file(vocab_dir).read().strip().split('\n') with open_file(vocab_dir) as fp: # 如果是py2 则每个值都转化为unicode words = [native_content(_.strip()) for _ in fp.readlines()] word_to_id = dict(zip(words, range(len(words)))) return words, word_to_id def read_category(): """读取分类目录,固定""" categories = ['体育', '财经', '房产', '家居', '教育', '科技', '时尚', '时政', '游戏', '娱乐'] categories = [native_content(x) for x in categories] cat_to_id = dict(zip(categories, range(len(categories)))) return categories, cat_to_id def to_words(content, words): """将id表示的内容转换为文字""" return ''.join(words[x] for x in content) def process_file(filename, word_to_id, cat_to_id, max_length=600): """将文件转换为id表示""" contents, labels = read_file(filename) data_id, label_id = [], [] for i in range(len(contents)): data_id.append([word_to_id[x] for x in contents[i] if x in word_to_id]) label_id.append(cat_to_id[labels[i]]) # 使用keras提供的pad_sequences来将文本pad为固定长度 x_pad = kr.preprocessing.sequence.pad_sequences(data_id, max_length) y_pad = kr.utils.to_categorical(label_id, num_classes=len(cat_to_id))# 将标签转换为one-hot表示 return x_pad, y_pad def batch_iter(x, y, batch_size=64): """生成批次数据""" data_len = len(x) num_batch = int((data_len - 1) / batch_size) + 1 indices = np.random.permutation(np.arange(data_len)) x_shuffle = x[indices] y_shuffle = y[indices] for i in range(num_batch): start_id = i * batch_size end_id = min((i + 1) * batch_size, data_len) yield x_shuffle[start_id:end_id], y_shuffle[start_id:end_id]

  • 2.IMDB
    2.1 下载数据
import tensorflow as tf from tensorflow import keras import numpy as np # 下载数据集 imdb = keras.datasets.imdb # num_words=10000 会保留训练数据中出现频次在前 10000 位的字词。为确保数据规模处于可管理的水平,罕见字词将被舍弃。 (train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000)

【NLP实践-数据集探索】2.2 探索数据
2.2.1 初探数据
每个样本都是一个整数数组,表示影评中的字词。每个标签都是整数值 0 或 1,其中 0 表示负面影评,1 表示正面影评。
# 查看样本数 print("Training entries: {}, labels: {}".format(len(train_data), len(train_labels))) # 查看第一个样本。影评文本已转换为整数,其中每个整数都表示字典中的一个特定字词。 print(train_data[0]) # 查看第一条和第二条影评中的字词数。影评的长度可能会有所不同,由于神经网络的输入必须具有相同长度,因此需要解决此问题。 len(train_data[0]), len(train_data[1])

2.2.2 将整数转换为字词
了解如何将整数转换回文本可能很有用。在以下代码中,我们将创建一个辅助函数来查询包含整数到字符串映射的字典对象。
# A dictionary mapping words to an integer index word_index = imdb.get_word_index() # The first indices are reserved word_index = {k:(v+3) for k,v in word_index.items()} word_index[""] = 0 word_index[""] = 1 word_index[""] = 2# unknown word_index[""] = 3 reverse_word_index = dict([(value, key) for (key, value) in word_index.items()]) def decode_review(text): return ' '.join([reverse_word_index.get(i, '?') for i in text]) 使用 decode_review 函数显示第一条影评的文本:decode_review(train_data[0])

2.3 准备数据
2.3.1 维度填充
影评(整数数组)必须转换为张量,然后才能馈送到神经网络中。我们可以通过以下两种方法实现这种转换:
对数组进行独热编码,将它们转换为由 0 和 1 构成的向量。例如,序列 [3, 5] 将变成一个 10000 维的向量,除索引 3 和 5 转换为 1 之外,其余全转换为 0。然后,将它作为网络的第一层,一个可以处理浮点向量数据的密集层。不过,这种方法会占用大量内存,需要一个大小为 num_words * num_reviews 的矩阵。
或者,我们可以填充数组,使它们都具有相同的长度,然后创建一个形状为 max_length * num_reviews 的整数张量。我们可以使用一个能够处理这种形状的嵌入层作为网络中的第一层。
由于影评的长度必须相同,我们将使用 pad_sequences 函数将长度标准化:train_data = https://www.it610.com/article/keras.preprocessing.sequence.pad_sequences(train_data, value=word_index[""], padding='post', maxlen=256) test_data = https://www.it610.com/article/keras.preprocessing.sequence.pad_sequences(test_data, value=word_index[""], padding='post', maxlen=256) 查看前2条影评的长度:len(train_data[0]), len(train_data[1]) 查看经过填充之后的第一条影评:print(train_data[0])

2.3.2 划分训练集与验证集
从原始训练数据中分离出 10000 个样本,创建一个验证集。(之所以不创建测试集,是因为此次目标仅使用训练数据开发和调整模型,然后仅使用一次测试数据评估准确率。)
# 训练数据前10000条作为验证集 x_val = train_data[:10000] #验证集 partial_x_train = train_data[10000:] #训练集 y_val = train_labels[:10000] partial_y_train = train_labels[10000:]

2.4 构建模型
2.4.1 网络
# input shape is the vocabulary count used for the movie reviews (10,000 words) vocab_size = 10000 model = keras.Sequential() # Embedding 层:在整数编码的词汇表中查找每个字词-索引的嵌入向量。模型在接受训练时会学习这些向量。这些向量会向输出数组添加一个维度。生成的维度为:(batch, sequence, embedding)。 model.add(keras.layers.Embedding(vocab_size, 16)) # GlobalAveragePooling1D 层通过对序列维度求平均值,针对每个样本返回一个长度固定的输出向量。这样,模型便能够以尽可能简单的方式处理各种长度的输入。 model.add(keras.layers.GlobalAveragePooling1D()) model.add(keras.layers.Dense(16, activation=tf.nn.relu)) model.add(keras.layers.Dense(1, activation=tf.nn.sigmoid)) model.summary() #查看网络信息

2.4.2 损失函数和优化器
# binary_crossentropy 更适合处理概率问题,它可测量概率分布之间的“差距”,在本例中则为实际分布和预测之间的“差距”。 model.compile(optimizer=tf.train.AdamOptimizer(), loss='binary_crossentropy', metrics=['accuracy'])

2.5 训练模型
history = model.fit(partial_x_train, partial_y_train, epochs=40, batch_size=512, validation_data=https://www.it610.com/article/(x_val, y_val), verbose=1)

2.6 评估模型
# 模型会返回两个值:损失(表示误差的数字,越低越好)和准确率 results = model.evaluate(test_data, test_labels)

    推荐阅读