当前位置: 首页>前端>正文

0509打卡

对知识点进行了学习

中文情感分析

code environment

在 python3.x & Tensorflow1.x 下工作正常

语料的准备

语料的选择为 谭松波老师的评论语料,正负例各2000。属于较小的数据集

词向量的准备

本实验使用开源词向量chinese-word-vectors

选择知乎语料训练而成的Word Vector

模型:CNN

结构:
  1. 中文词Embedding
  2. 多个不同长度的定宽卷积核
  3. 最大池化层,每个滤波器输出仅取一个最大值
  4. 全连接

模型主要代码解析

数据预处理

def parse_fn(line_words, line_tag):
    # Encode in Bytes for TF
    words = [w.encode() for w in line_words.strip().split()]
    tag = line_tag.strip().encode()
    return words, tag
定义了一个名为parse_fn的函数,它接收line_wordsline_tag作为输入。它将line_words字符串按空格分隔成单词列表,去除每个单词的首尾空白,并使用encode()方法将其编码为字节类型。同样地,line_tag也去除了首尾空白并编码为字节类型。函数返回一个元组(words, tag)
def generator_fn(words, tags):
    with Path(words).open('r', encoding='utf-8') as f_words, Path(tags).open('r', encoding='utf-8') as f_tags:
        for line_words, line_tag in zip_words, f_tags):
            yield parse_fn(line_words, line_tag)
定义了一个名为generator_fn的函数,它接收wordstags作为输入。使用Path打开两个指定路径的文件以供读取(采用UTF-8编码)。通过使用zip同时迭代这两个文件的每行。对于每一行,使用parse_fnline_wordsline_tag进行处理,并使用yield返回结果。该函数作为生成器,产生包含(words, tag)元组的值。
def input_fn(words_path, tags_path, params=None, shuffle_and_repeat=False):
    params = params if params is not None else {}
这个函数定义了输入数据的函数。它接受words_pathtags_path作为输入参数,params用于指定其他参数,默认为None
shapes = ([None], ())  # shape of every sample
types = (tf.string, tf.string)
defaults = ('<pad>', '')
定义了数据样本的形状(shapes),类型(types)和默认值(defaults)。每个样本由一个长度可变的字符串列表和一个字符串标签组成。
dataset = tf.data.Dataset.from_generator(
    functools.partial(generator_fn, words_path, tags_path),
    output_shapes=shapes, output_types=types).map(lambda w, t: (w[:params.get('nwords', 300)], t))
创建了一个tf.data.Dataset对象。使用tf.data.Dataset.from_generator从生成器函数generator_fn创建数据集。functools.partialwords_pathtags_path作为参数传递给generator_fn。指定了输出的形状和类型。通过map方法对每个样本进行处理,只保留前params.get('nwords',300)个单词,并保留原始的标签。
if shuffle_and_repeat:
    dataset = dataset.shuffle(params['buffer']).repeat(params['epochs'])
如果shuffle_repeatTrue,则对数据集进行洗牌和重复操作。使用shuffle方法对数据集进行洗牌,并设置缓冲区大小为params['buffer']。然后使用repeat方法对数据集进行重复操作,重复次数为params['epochs']
dataset = (dataset
           .padded_batch(params.get('batch_size', 20), ([params.get('nwords', 300)], ()), defaults)
           .prefetch(1))
对数据集进行填充批处理操作。使用padded_batch方法将数据集分成指定的批次大小,并在不同批次中对长度不同的样本进行填充。批次大小由params.get('batch_size', 20)指定,默认为20。填充的形状为([params.get('nwords', 300)], ()),其中params.get('nwords', 300)表示单词的最大数量,默认为300。未指定填充值的情况下,将使用默认值('<pad>', '')进行填充。最后,使用prefetch方法提前加载一批数据用于后续训练的处理。
return dataset
返回处理后的数据集。

模型定义

def model_fn(features, labels, mode, params):
    if isinstance(features, dict):
        features = features['words']
定义了模型函数model_fn,它接收featureslabelsmodeparams作为输入参数。如果features是一个字典,则将其赋值为features['words']
dropout = params.get('dropout', 0.5)
training (mode == tf.estimator.ModeKeys.TRAIN)
vocab_words = tf.contrib.lookup.index_table_from_file(
    params['words'], num_oov_buckets=params['num_oov_buckets'])
params中获取dropout的值(默认为0.5)。根据mode是否为训练模式,设置training变量为TrueFalse。使用tf.contrib.lookup.index_table_from_file根据文件params['words']创建一个单词的索引表,并指定了OOV(Out-of-vocabulary)桶的数量为params['num_oov_buckets']
with Path(params['tags']).open() as f:
    indices = [idx for idx, tag in enumerate(f)]
    num_tags = len(indices)
使用Path打开文件params['tags'],并遍历其中的行,获取每个标签的索引。计算得到标签的数量num_tags
word_ids = vocab_words.lookup(features)
w2v = np.load(params['w2v'])['embeddings']
w2v_var = np.vstack([w2v, [[0.] * params['dim']]])
w2v_var = tf.Variable(w2v_var, dtype=tf.float32, trainable=False)
embeddings = tf.nn.embedding_lookup(w2v_var, word_ids)
使用单词索引表vocab_words将输入的features转换预训练的词向量w2v,并创建一个tf.Variable变量w2v_var保存词向量。使用tf.nn.embedding_lookup根据单词ID查找对应的词向量,并得到嵌入矩阵embeddings
embeddings = tf.layers.dropout(embeddings, rate=dropout, training=training)
embeddings_expanded = tf.expand_dims(embeddings, -1)
使用tf.layers.dropout对嵌入矩阵embeddings进行丢弃操作,以减少过拟合。然后,使用tf.expand_dims在最后一维上添加一个维度。
pooled_outputs = []
for i, filter_size in enumerate(params['filter_sizes']):
    conv2 = tf.layers.conv2d(embeddings_expanded, params['num_filters'], kernel_size=[filter_size, params['dim']],
                             activation=tf.nn.relu, name='conv-{}'.format(i))
    pooled = tf.layers.max_pooling2d(inputs=conv2, pool_size=[params['nwords'] - filter_size + 1, 1],
                                     strides=[1, 1], name='pool-{}'.format(i))
    pooled_outputs.append(pooled)
定义一个空列表pooled_outputs,用于存储卷积层输出的池化结果。使用tf.layers.conv2d进行卷积操作,指定卷积核的大小为[filter_size, params['dim']],激活函数为ReLU,并命名tf.layers.max_pooling2d进行最大池化操作,指定池化窗口的大小为[params['nwords_size + 1, 1],步幅为[ 1],命名为'pool-{}'.format(i)。将池化结果pooled加入到pooled_outputs列表中。
num_total_filters = params['num_filters'] * len(params['filter_sizes'])
h_poll = tf.concat(pooled_outputs, 3)
output = tf.reshape(h_poll, [-1,```
计算总的过滤器数量`num_total_filters`,即`params['num_filters']`乘以卷积核尺寸的种类数。使用`tf.concat`在第三维上将所有池化结果拼接起来,得形状为`[batch_size, nwords, num_total_filters]`的`h_pool`。然后,使用``将`h_pool`展平成形状为`[-1, num_total_filters]`的张量。最后,再次使用`tf.layers.dropout`对输出进行丢弃操作。

```python
logits = tf.layers.dense(output, num_tags)
pred_ids = tf.argmax(input=logits, axis=1)
使用全连接层tf.layers.dense将输出转换为具有num_tags个输出节点的向量。使用tf.argmaxaxis=1上找出预测值索引,得到最终的预测标签索引pred_ids
if mode == tf.estimator.ModeKeys.PREDICT:
    reversed_tags = tf.contrib.lookup.index_to_string_table_from_file(params['tags'])
    pred_labels = reversed_tags.lookup(tf.argmax(inputits, axis=1))
    predictions = {
        'classes_id': pred        'labels': pred_labels
    }
    return tf.estimator.EstimatorSpec(mode, predictions=predictions)
else:
    tags_table = tf.contrib.lookup.index_table_from_file(params['tags tags = tags_table.lookup(labels)
    loss = tf.losses.sparse_softmax_cross_entropy(labels=tags, logits=logits)
如果是预测模式(mode == tf.estimator.ModeKeys.PREDICT),则加载标签反查表reversed_tags,将预测标签索引转换为实际标签,并构建预测结果字典predictions。之后,返回一个tf.estimator.EstimatorSpec对象,其中包含预测结果。如果不是预测模式,则使用标签索引表tags_table将实际标签转换为索引形式,并通过sparse_softmax_cross_entropy计算损失值loss
metrics = {
    'acc': tf.metrics.accuracy(tags, pred_ids),
    'precision': tf.metrics.precision(tags, pred_ids),
    'recall': tf.metrics.recall(tags, pred_ids)
}

for metric_name, op in metrics.items():
    tf.summary.scalar(metric_name, op[1])
定义了评估指标metrics,包括准确率('acc'),精确度('precision')和召回率('recall')。使用tf.metrics.accuracytf.metrics.precisiontf.metrics.recall`计算这些指标,并将其添加到TensorBoard的摘要中。
if mode == tf.estimator.ModeKeys.TRAIN:
    train_op = tf.train.AdamOptimizer().minimize(
        loss, global_step=tf.train.get_or_create_global_step())
    return tf.estimator.EstimatorSpec(mode, loss=loss, train_op=train_op)
elif mode == tf.estimator.ModeKeys.EVAL:
    return tf.estimator.EstimatorSpec(
        mode, loss=loss, eval_metric_ops=metrics)
如果是训练模式,则创建一个Adam优化器并最小化损失函数。返回一个tf.estimator.EstimatorSpec对象,其中包括损失值loss和训练操作train_op。如果是评估模式,则返回一个包含损失值loss和评估指标eval_metric_opstf.estimator.EstimatorSpec对象。

训练过程

if __name__ == '__main__':
	params = {
    'dim': 300,
    'nwords': 300,
    'filter_sizes': [2, 3, 4],
    'num_filters': 64,
    'dropout': 0.6,
    'num_oov_buckets': 1,
    'epochs': 50,
    'batch_size': 20,
    'buffer': 3500,
    'words': str(Path(DATA_DIR, 'vocab.words.txt')),
    'tags': str(Path(DATA_DIR, 'vocab.labels.txt')),
    'w2v': str(Path(DATA_DIR, 'w2v.npz'))
}
定义一个名为params的字典,其中包含了一组模型超参数。这些超参数包括词向量的维度dim、句子最大长度nwords、卷积核大小的列表filter_sizes、卷积核数量num_filters、丢弃率dropout、OOV桶的数量num_oov_buckets、训练轮数epochs、批处理大小batch_size、缓冲区大小buffer以及用于存储词汇表、标签和词向量的文件路径。
with Path('../../../Mutual-AI/model/chinese_sentiment_analysis/params.json').open('w') as f:
    json.dump(params, f, indent=4, sort_keys=True)
使用Path打开文件'../../../Mutual-AI/model/chinese_sentiment_analysis/params.json',以写式打开文件。然后使用json.dump以JSON格式写入文件中,缩进为4个空格,按键排序。
def fwords(name):
    return str(Path(DATA_DIR, '{}.words.txt'.format(name)))
定义了一个名为fwords的函数,它接收一个字符串参数name,并返回使用name构造路径的结果。路径由DATA_DIR{}.words.txt组成,其中{}将被替换为函数的参数name
def ftags(name):
    return str(Path(DATA_DIR, '{}.txt'.format(name)))
定义了一个名为ftags的,它接收一个字符串参数name,并返回使用name构造路径的结果。路径由{}将被替换为函数的参数name
train_inpf = functools.partial(input_fn, fwords('train'),
                               params, shuffle_and_repeat=True)
eval_inpf = functools.partial(input_fn, fwords('eval'), ftags('eval'))
创建了两个特殊函数``eval_inpf,它们是通过部分应input_fn函数获得的。train_inpf用于训练数据集,会将fwords('train')ftags('train')作为输入路径,并附带其他参数paramsshuffle_and_repeat=Trueeval_inpf用于评估数据集,会将fwords('eval')ftags('eval')作为输入路径。
cfg = tf.estimator.RunConfig(save_checkpoints_secs=10)
estimator = tf.estimator.Estimator(model_fn, 'results/model', cfg, params)
Path(estimator.eval_dir()).mkdir(parents=True, exist_ok=True)
创建了一个tf.estimator.RunConfig对象cfg,用于配置训练的运行环境,设置每隔10秒保存检查点。然后,使用这个运行配置、模型函数model_fn、模型保存路径'results/model'和超参数params创建了一个tf.estimator.Estimatorestimator.eval_dir()获取评估结果的保存路径,并使用Path`创建该路径的父目录,确保路径存在。
train_spec = tf.estimator.TrSpec(input_fn=train_inpf)
eval_spec = tf.EvalSpec(input_fn=eval_inpf, throttle_secs=10)
创建了训练规格train_spec和评估规格eval_specTrainSpectrain_inpf作为输入函数进行进行评估,其中还设置了每隔10秒进行一次.train_and_evaluate(estimator, train_spec, eval_spec) tf.estimator.train_and_evaluate函数同时进行训和eval_spec`传递给该函数。
def write_predictions(name):
    Path('results/score').mkdir(parents=True, exist_ok=True)
    with Path('results/score/{}.preds.txt'.format(name)).open('wb') as f:
        test_inpf = functools.partial(input_fn, fwords(name), ftags(name))
        golds_gen = generator_fn(fwords(name), ftags(name))
        preds_gen = estimator.predict(test_inpf)
        for golds, preds in zip(golds_gen, preds_gen):
            (words, tag) =
            f.write' '.join([tag, preds['labels'], b''.join(words)]) + b'\n')


for name in ['train', 'eval']:
    write_predictions(name)
定义了一个名为write_predictions的函数,用于将预测结果写入文件。先创建保存预测结果的目录results/score,然后使用Path打开文件results/score/{}.preds.txt进行写操作。在循环中,通过部分应用的方式创建输入函数test_inpf,并分别生成原始标签和预测结果的生成器。通过遍历这两个生成器,将数据写入文件中。
最后,对['train', 'eval']中的每个名称调用write_predictions函数,分别生成训练集和评估集的预测结果文件。
模型效果输出:
precision    recall  f1-score   support

         POS       0.91      0.87      0.89       400
         NEG       0.88      0.91      0.89       400

   micro avg       0.89      0.89      0.89       800
   macro avg       0.89      0.89      0.89       800
weighted avg       0.89      0.89      0.89       800

https://www.xamrdz.com/web/29v1951526.html

相关文章: