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 = []
    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> 来将所有文本pad为同一长度
    words = ['<PAD>'] + 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)

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["<PAD>"] = 0
word_index["<START>"] = 1
word_index["<UNK>"] = 2  # unknown
word_index["<UNUSED>"] = 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 = keras.preprocessing.sequence.pad_sequences(train_data,
                                                        value=word_index["<PAD>"],
                                                        padding='post',
                                                        maxlen=256)
 
test_data = keras.preprocessing.sequence.pad_sequences(test_data,
                                                       value=word_index["<PAD>"],
                                                       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=(x_val, y_val),
                    verbose=1)

2.6 评估模型

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

没有更多推荐了,返回首页