<jupyter_start><jupyter_text>自然语言处理实战 —— 文本分类文本分类是自然语言处理（NLP）领域的重要研究领域。文本分类指将文本按照一定的分类体系或标准进行分类标记，包括二分类和多分类等。在人工智能浪潮席卷全球的今天，文本分类技术已经被广泛地应用在情感分析、文本审核、广告过滤和反黄识别等 NLP 领域。现阶段的文本分类模型种类繁多，既有机器学习中的朴素贝叶斯模型、SVM 等，也有深度学习中的各种模型，比如经典的 CNN、RNN，以及它们的变形，如 CNN-LSTM 等。本实践首先介绍 ModelArts 的文本分类功能，之后使用 BERT 模型进行文本分类任务——中文文本情感分析。  ModelArts 文本分类功能本部分将介绍通过 ModelArts 的文本分类标注功能：对文本的内容按照标签进行分类处理。登录 ModelArts 管理控制台，在左侧菜单栏中选择`数据标注`，进入`数据集`管理页面。点击`创建数据集`，准备用于数据标注的文本数据。![](./img/data_tagging.png) 准备未标注数据集首先需要在 OBS 中创建一个数据集，后续的操作如标注数据、数据集发布等，都是基于创建和管理的数据集。OBS 链接在这里：https://www.huaweicloud.com/product/obs0.html数据标注功能需要获取访问 OBS 权限，在未进行委托授权之前，无法使用此功能。需要可以在`数据标注`页面，单击`服务授权`，由具备授权的账号`同意授权`后，即可使用。创建用于存储数据的 OBS 桶及文件夹。本实践中桶名设定为`classification-tagging`，**请用户建立新桶并自定义命名，OBS桶名全局唯一，若创建时桶名冲突，请选择其他不冲突桶名**。桶创建成功后，在桶中创建标注输入和标注输出的文件夹，并将用于标注是文本文件上传到输入文件夹中。文本标注文件的要求为：**文件格式要求 txt 或者 csv，文件大小不超过 8M，以换行符作为分隔符，每行数据代表一个标注对象。**在本实践中使用的示例标注文件`text.txt`可以[点此下载](https://modelarts-labs.obs.cn-north-1.myhuaweicloud.com/notebook/DL_nlp_text_classification/text.tar.gz)，解压后可上传到输入文件夹中按照本案例步骤使用。在本实践中创建文件夹结构示例如下：```tagging   │   ├─input   │       └─text.txt   └─output```其中- `input`   为文本分类输入文件夹- `text.txt`   为文本分类输入文本文件- `output`   为文本分类输出文件夹创建文本分类任务数据集，如下图所示![](./img/tagging_classification_1.png)注意创建参数- 名称：可自定义数据集名称，本案例中设定为`classification-tagging`- 数据集输入位置：本案例中设定为`/classification-tagging/tagging/input/`- 数据集输出位置：本案例中设定为`/classification-tagging/tagging/output/`- 标注场景：选择`文本`- 标注类型：选择`文本分类`- 添加标签集：可自定义标签名称、个数、颜色。本案例中设定两个分类标签：`正面`标签为红色；`负面`标签为绿色。![](./img/label_color.png)完成以上设定后，点击右下角`创建`。文本分类数据集创建完成后，系统自动跳转至数据集管理页面。![](./img/tagging_classification_2.png)点击数据集名称，进入标注界面。选择未标注对象，点击标签进行标注，如图所示![](./img/tagging_classification_3.png)选择标注对象：`那场比赛易建联打得真好！`，从标签集选择`正面`标签，然后点击下方`保存当前页`进行保存。继续选择其他标注对象，按上述方法进行标注。数据全部标注完成后（本样例中仅提供五条分类文本），点击`已标注`可查看标注结果。![](./img/tagging_classification_4.png)点击`返回数据集`，可以看到数据集已全部标注成功。![](./img/tagging_classification_5.png)针对刚创建的数据集（未发布前），无数据集版本信息，必须执行发布操作后，才能应用于模型开发或训练。点击`发布`，可以编辑版本名称，本案例中为默认`V001`。![](./img/tagging_classification_6.png)发布成功如图所示。![](./img/tagging_classification_7.png)可以查看数据集版本的 “名称”、 “状态”、 “文件总数”、 “已标注文件个数”，并在左侧的 “演进过程”中查看版本的发布时间。随后可以使用标注成功的数据集，标注结果储存在`output`文件夹中。后续 ModelArts 将会上线智能标注功能，相信大家已经体验过第二期实战的图像智能标注，能够快速完成数据标注，节省70%以上的标注时间。智能标注是指基于当前标注阶段的标签及学习训练，选中系统中已有的模型进行智能标注，快速完成剩余数据的标注操作。请持续关注数据标注功能。  进入ModelArts点击如下链接：https://www.huaweicloud.com/product/modelarts.html ， 进入ModelArts主页。点击“立即使用”按钮，输入用户名和密码登录，进入ModelArts使用页面。 创建ModelArts notebook下面，我们在ModelArts中创建一个notebook开发环境，ModelArts notebook提供网页版的Python开发环境，可以方便的编写、运行代码，并查看运行结果。第一步：在ModelArts服务主界面依次点击“开发环境”、“创建”![create_nb_create_button](./img/create_nb_create_button.png)第二步：填写notebook所需的参数：| 参数 | 说明 || - - - - - | - - - - - || 计费方式 | 按需计费  || 名称 | Notebook实例名称，如 text_sentiment_analysis || 工作环境 | Python3 || 资源池 | 选择"公共资源池"即可 || 类型 | 本案例使用较为复杂的深度神经网络模型，需要较高算力，选择"GPU" || 规格 | 选择"8核 &124; 64GiB &124; 1*p100" || 存储配置 | 选择EVS，磁盘规格5GB |第三步：配置好notebook参数后，点击下一步，进入notebook信息预览。确认无误后，点击“立即创建”![create_nb_creation_summary](./img/create_nb_creation_summary.png)第四步：创建完成后，返回开发环境主界面，等待Notebook创建完毕后，打开Notebook，进行下一步操作。![modelarts_notebook_index](./img/modelarts_notebook_index.png) 在ModelArts中创建开发环境接下来，我们创建一个实际的开发环境，用于后续的实验步骤。第一步：点击下图所示的“打开”按钮，进入刚刚创建的Notebook![inter_dev_env](img/enter_dev_env.png)第二步：创建一个Python3环境的的Notebook。点击右上角的"New"，然后创建TensorFlow 1.13.1开发环境。第三步：点击左上方的文件名"Untitled"，并输入一个与本实验相关的名称![notebook_untitled_filename](./img/notebook_untitled_filename.png)![notebook_name_the_ipynb](./img/notebook_name_the_ipynb.png) 在Notebook中编写并执行代码在Notebook中，我们输入一个简单的打印语句，然后点击上方的运行按钮，可以查看语句执行的结果：![run_helloworld](./img/run_helloworld.png)  文本分类——中文文本情感分析文本情感分析是指对带有主观性的观点、喜好、情感等文本进行分析和挖掘。最初的文本情感分析来自对带有情感色彩的词语的分析，例如，“美好”是带有褒义色彩的词语，而“丑陋”是带有贬义色彩的词语。随着互联网上大量的带有情感色彩的主观性文本的出现，研究者们逐渐从简单的情感词语的分析研究扩展到更为复杂的完整情感文本的研究。为了定量表示情感偏向，一般使用0到1之间的一个浮点数给文本打上情感标签，越接近1表示文本的情感越正向，越接近0表示情感越负向。 数据集在本实战中，使用的中文文本分类的数据集来自谭松波老师从某酒店网站上整理的酒店评论数据。数据集共7000多条评论数据，5000多条正向评论，2000多条负向评论。数据格式：| 字段 | label  | review     | | ---- | ------- | ---------- | | 含义 | 情感标签  | 评论文本 |    BERT 模型本实践使用 NLP 领域最新最强大的 **BERT** 模型。中文**BERT-Base,Chinese**预训练模型，可以从链接[BERT-Base, Chinese](https://storage.googleapis.com/bert_models/2018_11_03/chinese_L-12_H-768_A-12.zip)下载使用。 准备源代码和数据准备案例所需的源代码和数据，相关资源已经保存在 OBS 中，我们通过 ModelArts SDK 将资源下载到本地。<jupyter_code>from modelarts.session import Session
session = Session()

if session.region_name == 'cn-north-1':
    bucket_path = 'modelarts-labs/notebook/DL_nlp_text_classification/text_classification.tar.gz'
    
elif session.region_name == 'cn-north-4':
    bucket_path = 'modelarts-labs-bj4/notebook/DL_nlp_text_classification/text_classification.tar.gz'
else:
    print("请更换地区到北京一或北京四")
    
session.download_data(bucket_path=bucket_path, path='./text_classification.tar.gz')

!ls -la<jupyter_output>Successfully download file modelarts-labs/notebook/DL_nlp_text_classification/text_classification.tar.gz from OBS to local ./text_classification.tar.gz
total 374440
drwxrwxrwx  4 ma-user ma-group      4096 Sep 12 10:29 .
drwsrwsr-x 22 ma-user ma-group      4096 Sep 12 09:42 ..
drwxr-x---  2 ma-user ma-group      4096 Sep 12 09:39 .ipynb_checkpoints
-rw-r-----  1 ma-user ma-group     33828 Sep 12 10:23 text_classification.ipynb
-rw-r-----  1 ma-user ma-group 383370868 Sep 12 10:29 text_classification.tar.gz
drwx------  2 ma-user ma-group      4096 Sep 12 10:04 .Trash-1000<jupyter_text>解压从obs下载的压缩包，解压后删除压缩包。<jupyter_code>!tar xf ./text_classification.tar.gz

!rm ./text_classification.tar.gz

!ls -la<jupyter_output>total 56
drwxrwxrwx  5 ma-user ma-group  4096 Sep 12 10:29 .
drwsrwsr-x 22 ma-user ma-group  4096 Sep 12 09:42 ..
drwxr-x---  2 ma-user ma-group  4096 Sep 12 09:39 .ipynb_checkpoints
drwxr-x---  6 ma-user ma-group  4096 Sep 11 11:28 text_classification
-rw-r-----  1 ma-user ma-group 33828 Sep 12 10:23 text_classification.ipynb
drwx------  2 ma-user ma-group  4096 Sep 12 10:04 .Trash-1000<jupyter_text>导入依赖包<jupyter_code>import os
import re
import pandas as pd
import tensorflow as tf
from tensorflow import keras
from sklearn.model_selection import train_test_split
from text_classification.bert import modeling, optimization, tokenization

tf.logging.set_verbosity(tf.logging.INFO)<jupyter_output><empty_output><jupyter_text>定义数据和模型路径<jupyter_code>data_dir = './text_classification/data/'
output_dir = './text_classification/output/'
vocab_file = './text_classification/chinese_L-12_H-768_A-12/vocab.txt'
bert_config_file = './text_classification/chinese_L-12_H-768_A-12/bert_config.json'
init_checkpoint = './text_classification/chinese_L-12_H-768_A-12/bert_model.ckpt'<jupyter_output><empty_output><jupyter_text>设置模型参数<jupyter_code>batch_size = 64
learning_rate = 2e-5 
num_train_epochs = 10
warmup_proportion = 0.1
save_checkpoints_steps = 500 
save_summary_steps = 100 
max_seq_length = 128
label_list = [0, 1]<jupyter_output><empty_output><jupyter_text>读取数据集需要获取非倾斜的数据集，使标签的比例基本相等。随机展示训练集样本20条。<jupyter_code>def get_balance_corpus(corpus_size, corpus_pos, corpus_neg):
    sample_size = corpus_size // 2
    pd_corpus_balance = pd.concat([corpus_pos.sample(sample_size, replace=corpus_pos.shape[0]<sample_size), \
                                   corpus_neg.sample(sample_size, replace=corpus_neg.shape[0]<sample_size)])
    
    print('总评论数：%d' % pd_corpus_balance.shape[0])
    print('正向评论：%d' % pd_corpus_balance[pd_corpus_balance.label==1].shape[0])
    print('负向评论：%d' % pd_corpus_balance[pd_corpus_balance.label==0].shape[0])    
    
    return pd_corpus_balance

reviews_all = pd.read_csv(data_dir + 'ChnSentiCorp_htl_all.csv')

pd_positive = reviews_all[reviews_all.label==1]
pd_negative = reviews_all[reviews_all.label==0]

reviews_4000 = get_balance_corpus(4000, pd_positive, pd_negative)

train, test = train_test_split(reviews_4000, test_size=0.2)

print('\n训练样本示例\n')
train.sample(20)<jupyter_output>总评论数：4000
正向评论：2000
负向评论：2000

训练样本示例<jupyter_text>读取BERT预训练模型中文字典<jupyter_code>tokenizer = tokenization.FullTokenizer(vocab_file=vocab_file, do_lower_case=False)

tokenizer.tokenize("今天的天气真好！")<jupyter_output><empty_output><jupyter_text>创建数据输入类<jupyter_code>class InputExample(object):

    def __init__(self, guid, text_a, text_b=None, label=None):
        self.guid = guid
        self.text_a = text_a
        self.text_b = text_b
        self.label = label

class InputFeatures(object):

    def __init__(self,
               input_ids,
               input_mask,
               segment_ids,
               label_id,
               is_real_example=True):
        self.input_ids = input_ids
        self.input_mask = input_mask
        self.segment_ids = segment_ids
        self.label_id = label_id
        self.is_real_example = is_real_example
    
class PaddingInputExample(object):
    pass


DATA_COLUMN = 'review'
LABEL_COLUMN = 'label'

train_InputExamples = train.apply(lambda x: InputExample(guid=None,  
                                                         text_a = x[DATA_COLUMN], 
                                                         text_b = None, 
                                                         label = x[LABEL_COLUMN]), axis = 1)

test_InputExamples = test.apply(lambda x: InputExample(guid=None, 
                                                       text_a = x[DATA_COLUMN], 
                                                       text_b = None, 
                                                       label = x[LABEL_COLUMN]), axis = 1)<jupyter_output><empty_output><jupyter_text>转换为 BERT 输入向量打印前5个样例文本及其字向量、文本向量、位置向量和标签。<jupyter_code>def truncate_seq_pair(tokens_a, tokens_b, max_length):
    while True:
        total_length = len(tokens_a) + len(tokens_b)
        if total_length <= max_length:
            break
        if len(tokens_a) > len(tokens_b):
            tokens_a.pop()
        else:
            tokens_b.pop()


def convert_single_example(ex_index, example, label_list, max_seq_length,
                           tokenizer):

    if isinstance(example, PaddingInputExample):
        return InputFeatures(
            input_ids=[0] * max_seq_length,
            input_mask=[0] * max_seq_length,
            segment_ids=[0] * max_seq_length,
            label_id=0,
            is_real_example=False)
    
    label_map = {}
    for (i, label) in enumerate(label_list):
        label_map[label] = i

    tokens_a = tokenizer.tokenize(example.text_a)
    tokens_b = None
    if example.text_b:
        tokens_b = tokenizer.tokenize(example.text_b)

    if tokens_b:
        truncate_seq_pair(tokens_a, tokens_b, max_seq_length - 3)
    else:
        if len(tokens_a) > max_seq_length - 2:
            tokens_a = tokens_a[0:(max_seq_length - 2)]

    tokens = []
    segment_ids = []
    tokens.append("[CLS]") # 句头添加 [CLS] 标志
    segment_ids.append(0)
    for token in tokens_a:
        tokens.append(token)
        segment_ids.append(0)
    tokens.append("[SEP]") # 句尾添加[SEP] 标志
    segment_ids.append(0)

    if tokens_b:
        for token in tokens_b:
            tokens.append(token)
            segment_ids.append(1)
        tokens.append("[SEP]")
        segment_ids.append(1)

    input_ids = tokenizer.convert_tokens_to_ids(tokens)  
    input_mask = [1] * len(input_ids)

    while len(input_ids) < max_seq_length:
        input_ids.append(0)
        input_mask.append(0)
        segment_ids.append(0)

    assert len(input_ids) == max_seq_length
    assert len(input_mask) == max_seq_length
    assert len(segment_ids) == max_seq_length

    label_id = label_map[example.label]
    
    if ex_index < 5:
        tf.logging.info("*** 示例 ***")
        tf.logging.info("guid: %s" % (example.guid)) 
        tf.logging.info("tokens: %s" % " ".join([tokenization.printable_text(x) for x in tokens])) 
        tf.logging.info("input_ids: %s" % " ".join([str(x) for x in input_ids]))  
        tf.logging.info("input_mask: %s" % " ".join([str(x) for x in input_mask])) 
        tf.logging.info("segment_ids: %s" % " ".join([str(x) for x in segment_ids])) 
        tf.logging.info("label: %s (id = %d)" % (example.label, label_id)) 

    feature = InputFeatures(
        input_ids=input_ids,
        input_mask=input_mask,
        segment_ids=segment_ids,
        label_id=label_id,
        is_real_example=True)
    return feature

def convert_examples_to_features(examples, label_list, max_seq_length, tokenizer):

    features = []
    for (ex_index, example) in enumerate(examples):
        if ex_index % 10000 == 0:
            tf.logging.info("Writing example %d of %d" % (ex_index, len(examples)))

        feature = convert_single_example(ex_index, example, label_list,
                                     max_seq_length, tokenizer)

        features.append(feature)
    return features


train_features = convert_examples_to_features(train_InputExamples, label_list, max_seq_length, tokenizer)
test_features = convert_examples_to_features(test_InputExamples, label_list, max_seq_length, tokenizer)<jupyter_output>INFO:tensorflow:Writing example 0 of 3200
INFO:tensorflow:*** 示例 ***
INFO:tensorflow:guid: None
INFO:tensorflow:tokens: [CLS] 7 月 16 日 入 住 ， 是 在 携 程 买 的 半 自 助 自 由 行 ， 所 以 携 程 已 经 处 理 好 所 有 的 文 件 ， 只 提 供 了 通 行 证 就 顺 利 入 住 ， 非 常 快 ， 前 台 服 务 生 也 非 常 客 气 ， 很 专 业 。 酒 店 大 堂 有 很 贴 心 的 安 排 ， 大 人 在 办 理 入 住 的 时 候 ， 孩 子 可 以 在 一 旁 看 迪 士 尼 的 动 画 片 。 酒 店 的 房 间 住 2 个 大 人 2 个 孩 子 都 很 宽 敞 ， 到 [SEP]
INFO:tensorflow:input_ids: 101 128 3299 8121 3189 1057 857 8024 3221 1762 3025 4923 743 4638 1288 5632 1221 5632 4507 6121 8024 2792 809 3025 4923 2347 5307 1905 4415 1962 2792 3300 4638 3152 816 8024 1372 2990 897 749 6858 6121 6395 2218 7556 1164 1057 857 8024 7478 2382 2571 8024 1184 1378 3302 1218 4495 738 7478 2382 2145 3698 8024 2523 683 689 511 6983 2421 1920 1828 3300 2523 6585 2552 4638 2128 2961 8024 1920 782 1762 1215 4415 1057 857 4638 3198 952 8024 2111 2094 1377 809 1762 671 3178 4692 6832 1894 2225 4638 1220 4514 4275 511 6983 2421 4638 2791 7313 857 123 702 1920 782 123 702 2111 2094 6963 252[...]<jupyter_text>加载模型参数，构造模型结构<jupyter_code>bert_config = modeling.BertConfig.from_json_file(bert_config_file)

def create_model(bert_config, is_training, input_ids, input_mask, segment_ids,
                 labels, num_labels, use_one_hot_embeddings):

    model = modeling.BertModel(
        config=bert_config,
        is_training=is_training,
        input_ids=input_ids,
        input_mask=input_mask,
        token_type_ids=segment_ids,
        use_one_hot_embeddings=use_one_hot_embeddings)

    output_layer = model.get_pooled_output()
    hidden_size = output_layer.shape[-1].value

    output_weights = tf.get_variable(
        "output_weights", [num_labels, hidden_size],
        initializer=tf.truncated_normal_initializer(stddev=0.02))

    output_bias = tf.get_variable(
        "output_bias", [num_labels], initializer=tf.zeros_initializer())

    with tf.variable_scope("loss"):
        if is_training:
            output_layer = tf.nn.dropout(output_layer, keep_prob=0.9)

        logits = tf.matmul(output_layer, output_weights, transpose_b=True)
        logits = tf.nn.bias_add(logits, output_bias)

        probabilities = tf.nn.softmax(logits, axis=-1)
        log_probs = tf.nn.log_softmax(logits, axis=-1)

        one_hot_labels = tf.one_hot(labels, depth=num_labels, dtype=tf.float32)

        per_example_loss = -tf.reduce_sum(one_hot_labels * log_probs, axis=-1)

        loss = tf.reduce_mean(per_example_loss)
        return (loss, per_example_loss, logits, probabilities)


def model_fn_builder(bert_config, num_labels, init_checkpoint, learning_rate,
                     num_train_steps, num_warmup_steps):

    def model_fn(features, labels, mode, params):

        input_ids = features["input_ids"]
        input_mask = features["input_mask"]
        segment_ids = features["segment_ids"]
        label_ids = features["label_ids"]
        is_real_example = None
        if "is_real_example" in features:
            is_real_example = tf.cast(features["is_real_example"], dtype=tf.float32)
        else:
            is_real_example = tf.ones(tf.shape(label_ids), dtype=tf.float32)

        is_training = (mode == tf.estimator.ModeKeys.TRAIN)
        use_one_hot_embeddings = False

        (total_loss, per_example_loss, logits, probabilities) = create_model(
            bert_config, is_training, input_ids, input_mask, segment_ids, label_ids,
            num_labels, use_one_hot_embeddings)

        tvars = tf.trainable_variables()
        initialized_variable_names = {}
        scaffold_fn = None
    
        if init_checkpoint:
            (assignment_map, initialized_variable_names
            ) = modeling.get_assignment_map_from_checkpoint(tvars, init_checkpoint)
    
            tf.train.init_from_checkpoint(init_checkpoint, assignment_map)

        output_spec = None
   
        if mode == tf.estimator.ModeKeys.TRAIN:

            train_op = optimization.create_optimizer(
              total_loss, learning_rate, num_train_steps, num_warmup_steps, use_tpu=False)

            output_spec = tf.estimator.EstimatorSpec(
              mode=mode,
              loss=total_loss,
              train_op=train_op)

        elif mode == tf.estimator.ModeKeys.EVAL:

            def metric_fn(per_example_loss, label_ids, logits, is_real_example):
                predictions = tf.argmax(logits, axis=-1, output_type=tf.int32)
                accuracy = tf.metrics.accuracy(
                    labels=label_ids, predictions=predictions, weights=is_real_example)
                loss = tf.metrics.mean(values=per_example_loss, weights=is_real_example)
                return {
                    "eval_accuracy": accuracy,
                    "eval_loss": loss,
                }

            eval_metrics = metric_fn(per_example_loss, label_ids, logits, is_real_example)
            output_spec = tf.estimator.EstimatorSpec(
              mode=mode,
              loss=total_loss,
              eval_metric_ops=eval_metrics)
    
        else:
            output_spec = tf.estimator.EstimatorSpec(
              mode=mode,
              predictions={"probabilities": probabilities})
    
        return output_spec

    return model_fn


num_train_steps = int(len(train_features) / batch_size * num_train_epochs)
num_warmup_steps = int(num_train_steps * warmup_proportion)

model_fn = model_fn_builder(
    bert_config=bert_config,
    num_labels=len(label_list),
    learning_rate=learning_rate,
    init_checkpoint=init_checkpoint,
    num_train_steps=num_train_steps,
    num_warmup_steps=num_warmup_steps)<jupyter_output><empty_output><jupyter_text>模型训练<jupyter_code>def input_fn_builder(features, seq_length, is_training, drop_remainder):

    all_input_ids = []
    all_input_mask = []
    all_segment_ids = []
    all_label_ids = []

    for feature in features:
        all_input_ids.append(feature.input_ids)
        all_input_mask.append(feature.input_mask)
        all_segment_ids.append(feature.segment_ids)
        all_label_ids.append(feature.label_id)

    def input_fn(params):
        batch_size = params["batch_size"]

        num_examples = len(features)

        d = tf.data.Dataset.from_tensor_slices({
            "input_ids":
                tf.constant(
                    all_input_ids, shape=[num_examples, seq_length],
                    dtype=tf.int32),
            "input_mask":
                tf.constant(
                    all_input_mask,
                    shape=[num_examples, seq_length],
                    dtype=tf.int32),
            "segment_ids":
                tf.constant(
                    all_segment_ids,
                    shape=[num_examples, seq_length],
                    dtype=tf.int32),
            "label_ids":
                tf.constant(all_label_ids, shape=[num_examples], dtype=tf.int32),
        })
    
        if is_training:
            d = d.repeat()
            d = d.shuffle(buffer_size=100)

        d = d.batch(batch_size=batch_size, drop_remainder=drop_remainder)
        return d

    return input_fn


run_config = tf.estimator.RunConfig(
    model_dir=output_dir,
    save_summary_steps=save_summary_steps,
    save_checkpoints_steps=save_checkpoints_steps)

estimator = tf.estimator.Estimator(
    model_fn=model_fn,
    config=run_config,
    params={"batch_size": batch_size})

train_input_fn = input_fn_builder(
    features=train_features,
    seq_length=max_seq_length,
    is_training=True,
    drop_remainder=False) 

estimator.train(input_fn=train_input_fn, max_steps=num_train_steps)<jupyter_output>INFO:tensorflow:Using config: {'_model_dir': './text_classification/output/', '_tf_random_seed': None, '_save_summary_steps': 100, '_save_checkpoints_steps': 500, '_save_checkpoints_secs': None, '_session_config': allow_soft_placement: true
graph_options {
  rewrite_options {
    meta_optimizer_iterations: ONE
  }
}
, '_keep_checkpoint_max': 5, '_keep_checkpoint_every_n_hours': 10000, '_log_step_count_steps': 100, '_train_distribute': None, '_device_fn': None, '_protocol': None, '_eval_distribute': None, '_experimental_distribute': None, '_service': None, '_cluster_spec': <tensorflow.python.training.server_lib.ClusterSpec object at 0x7f222ad54da0>, '_task_type': 'worker', '_task_id': 0, '_global_id_in_cluster': 0, '_master': '', '_evaluation_master': '', '_is_chief': True, '_num_ps_replicas': 0, '_num_worker_replicas': 1}
INFO:tensorflow:Calling model_fn.
INFO:tensorflow:Done calling model_fn.
INFO:tensorflow:Create CheckpointSaverHook.
INFO:tensorflow:Graph was finalized.
INFO:tensorf[...]<jupyter_text>在测试集上测试，评估测试结果<jupyter_code>eval_input_fn = input_fn_builder(
    features=test_features,
    seq_length=max_seq_length,
    is_training=False,
    drop_remainder=False)

evaluate_info = estimator.evaluate(input_fn=eval_input_fn, steps=None)

print("\n打印测试评估指标")
for key in evaluate_info:
    print(key+' : '+str(evaluate_info[key]))<jupyter_output>INFO:tensorflow:Calling model_fn.
INFO:tensorflow:Done calling model_fn.
INFO:tensorflow:Starting evaluation at 2019-09-12-02:37:48
INFO:tensorflow:Graph was finalized.
INFO:tensorflow:Restoring parameters from ./text_classification/output/model.ckpt-500
INFO:tensorflow:Running local_init_op.
INFO:tensorflow:Done running local_init_op.
INFO:tensorflow:Finished evaluation at 2019-09-12-02:37:53
INFO:tensorflow:Saving dict for global step 500: eval_accuracy = 0.90625, eval_loss = 0.6071, global_step = 500, loss = 0.60426533
INFO:tensorflow:Saving 'checkpoint_path' summary for global step 500: ./text_classification/output/model.ckpt-500<jupyter_text>在线测试由以上训练得到模型进行在线测试，可以任意输入句子，进行文本情感分析。输入“再见”，结束在线文本情感分析。<jupyter_code>def getPrediction(in_sentences):
    labels = ["负面评价", "正面评价"]
    input_examples = [InputExample(guid="", text_a = x, text_b = None, label = 0) for x in in_sentences] # here, "" is just a dummy label
    input_features = convert_examples_to_features(input_examples, label_list, max_seq_length, tokenizer)
    predict_input_fn = input_fn_builder(features=input_features, seq_length=max_seq_length, is_training=False, drop_remainder=False)
    predictions = estimator.predict(predict_input_fn)
    for sentence, prediction in zip(in_sentences, predictions):
        print("\n评论：", sentence)
        print("得分：", prediction['probabilities'])
        print("评论情感分析：", labels[int(round(prediction['probabilities'][1]))])
    return 

def sentiment_analysis():
    while True:
        pred_sentences = [input()]
        if pred_sentences == ["再见"]:
            print("\n再见")
            return
        else:
            predictions = getPrediction(pred_sentences)

print("在线文本情感分析:\n")            
sentiment_analysis()<jupyter_output>在线文本情感分析:

前台服务态度很热情