外观
文本预处理
Jieba 分词库
基本使用
jieba 分词库是由一名百度工程师研发的中文分词库,分为三个模式:精确模式、全模式分词、搜索引擎模式。
import jieba
content = "人工神经网络也简称为神经网络,是一种模仿生物神经网络结构和功能的计算模型。"
result1 = jieba.cut(content) # 精确模式,对象为生成器
print(result1) # <generator object Tokenizer.cut at 0x000001EB61E63480>
result2 = jieba.lcut(content) # 精确模式,对象为列表,l代表list
print(result2)
# ['人工神经网络', '也', '简称', '为', '神经网络', ',', '是', '一种', '模仿', '生物',
# '神经网络', '结构', '和', '功能', '的', '计算', '模型', '。']
result3 = jieba.lcut(content, cut_all=True) # 全模式
print(result3)
# ['人工', '人工神经网络', '神经', '神经网', '神经网络', '网络', '也', '简称', '称为',
# '神经', '神经网', '神经网络', '网络', ',', '是', '一种', '模仿', '仿生', '生物', '神经',
# '神经网', '神经网络', '网络', '网络结构', '结构', '和', '功能', '的', '计算', '模型', '。']
result4 = jieba.lcut_for_search(content) # 搜索引擎模式
print(result4)
# ['人工', '神经', '网络', '神经网', '人工神经网络', '也', '简称', '为', '神经', '网络', '神经网',
# '神经网络', ',', '是', '一种', '模仿', '生物', '神经', '网络', '神经网', '神经网络', '结构', '和',
# '功能', '的', '计算', '模型', '。']jieba 分词库还可以自定义词典,词典的格式:
词语 词频(可省略) 词性(可省略)如:
计算模型 2 nimport jieba
content = "人工神经网络也简称为神经网络,是一种模仿生物神经网络结构和功能的计算模型。"
jieba.load_userdict("user_dicts.txt")
result = jieba.lcut(content)
print(result)
# ['人工神经网络', '也', '简称', '为', '神经网络', ',', '是', '一种', '模仿', '生物',
# '神经网络', '结构', '和', '功能', '的', '计算模型', '。']词性对照表
- a 形容词
- ad 副形词
- ag 形容词性语素
- an 名形词
- b 区别词
- c 连词
- d 副词
- df
- dg 副语素
- e 叹词
- f 方位词
- g 语素
- h 前接成分
- i 成语
- j 简称略称
- k 后接成分
- l 习用语
- m 数词
- mg
- mq 数量词
- n 名词
- ng 名词性语素
- nr 人名
- nrfg
- nrt
- ns 地名
- nt 机构团体名
- nz 其他专名
- o 拟声词
- p 介词
- q 量词
- r 代词
- rg 代词性语素
- rr 人称代词
- rz 指示代词
- s 处所词
- t 时间词
- tg 时语素
- u 助词
- ud 结构助词 得
- ug 时态助词
- uj 结构助词 的
- ul 时态助词 了
- uv 结构助词 地
- uz 时态助词 着
- v 动词
- vd 副动词
- vg 动词性语素
- vi 不及物动词
- vn 名动词
- vq
- x 非语素词
- y 语气词
- z 状态词
- zg
命名实体识别
我们通常将地名、人名、机构名等专有名词统称为命名实体。而命名实体识别(Named Entity Recognition,简称 NER),就是识别出一段文本可能存的命名实体。
如:鲁迅,浙江绍兴人,五四新文化运动的重要参与者,代表作朝花夕拾。
结果为“鲁迅(人名) / 浙江绍兴(地名)人 / 五四新文化运动(专有名词) / 重要参与者 / 代表作 / 朝花夕拾(专有名词)”。
同词汇一样,命名实体也是人类理解文本的基础单元,因此也是 AI 解决 NLP 领域高阶任务的重要基础环节。
词性标注
词性:语言中对词的一种分类方法,以语法特征为主要依据,兼顾词汇意义对词划分的结果,如动词、名词、形容词。
词性标注(Part-Of-Speech tagging)就是标注出一段文本中每个词汇的词性。
如:我爱自然语言处理。
结果为“我/rr,爱/v,自然语言/n,处理/vn”。
词性标注以分词为基础,是对文本语言的另一个角度的理解,因此也常常成为 AI 解决 NLP 领域高阶任务的重要基础环节。
使用 jieba 进行中文词性标注:
import jieba.posseg as pseg
content = "人工神经网络也简称为神经网络,是一种模仿生物神经网络结构和功能的计算模型。"
result = pseg.lcut(content)
for word, flag in result:
print(f"词汇:{word} 词性:{flag}")
# 词汇:人工神经网络 词性:n
# 词汇:也 词性:d
# 词汇:简称 词性:v
# 词汇:为 词性:p
# 词汇:神经网络 词性:n
# 词汇:, 词性:x
# 词汇:是 词性:v
# 词汇:一种 词性:m
# 词汇:模仿 词性:v
# 词汇:生物 词性:n
# 词汇:神经网络 词性:n
# 词汇:结构 词性:n
# 词汇:和 词性:c
# 词汇:功能 词性:n
# 词汇:的 词性:uj
# 词汇:计算 词性:v
# 词汇:模型 词性:n
# 词汇:。 词性:x文本张量表示方法
文本张量表示
将一段文本使用张量进行表示,其中一般将词汇表示成向量,称作词向量,再由各个词向量按顺序组成矩阵形成文本表示。
如“["人生", "该", "如何", "起头"]”可能转换的词向量:
[[1.32, 4,32, 0,32, 5.2],
[3.1, 5.43, 0.34, 3.2],
[3.21, 5.32, 2, 4.32],
[2.54, 7.32, 5.12, 9.54]]文本张量能够将文本表示成张量(矩阵)形式,能够使语言文本可以作为计算机处理程序的输入,进行接下来一系列的解析工作。
文本张量的表示方法有 one-hot 编码、Word2Vec、Word Embedding。
One Hot 词向量表示
one-hot 编码又称为独热编码,思想是将每个词表示成具有 n 个元素的向量,这个词向量只有一个元素是 1,其他都是 0。不同词汇,元素为 0 的位置也不同,n 的大小是整个语料中不同的词汇数量。
如:“["改变", "要", "如何", "起手"]”的编码为:
[[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]]通过 Python 编码实现:
import joblib
from tensorflow.keras.preprocessing.text import Tokenizer
# 准备语料 vocabs
vocabs = {"周杰伦", "陈奕迅", "王力宏", "李宗盛", "吴亦凡", "鹿晗"}
# 实例化词汇映射器Tokenizer
mytokenizer = Tokenizer()
# 使用映射器拟合现有文本数据 (内部生成 index_word word_index)
mytokenizer.fit_on_texts(vocabs)
# 查询单词idx 赋值 zero_list,生成 one-hot
for vocab in vocabs:
zero_list = [0] * len(vocabs)
idx = mytokenizer.word_index[vocab] - 1
zero_list[idx] = 1
print(vocab, "的onehot编码是", zero_list)
# 使用joblib工具保存映射器 joblib.dump()
mypath = "./mytokenizer"
joblib.dump(mytokenizer, mypath)
print("保存mytokenizer End")
# 注意1:字典没有顺序 onehot编码没有顺序 []-有序 {}-无序 区别
# 注意2:字典有的单词才有idx idx从1开始
# 注意3:查询没有注册的词会有异常 eg: 你好
print(mytokenizer.word_index)
print(mytokenizer.index_word)周杰伦 的onehot编码是 [1, 0, 0, 0, 0, 0]
王力宏 的onehot编码是 [0, 1, 0, 0, 0, 0]
陈奕迅 的onehot编码是 [0, 0, 1, 0, 0, 0]
吴亦凡 的onehot编码是 [0, 0, 0, 1, 0, 0]
李宗盛 的onehot编码是 [0, 0, 0, 0, 1, 0]
鹿晗 的onehot编码是 [0, 0, 0, 0, 0, 1]
保存mytokenizer End
{'周杰伦': 1, '王力宏': 2, '陈奕迅': 3, '吴亦凡': 4, '李宗盛': 5, '鹿晗': 6}
{1: '周杰伦', 2: '王力宏', 3: '陈奕迅', 4: '吴亦凡', 5: '李宗盛', 6: '鹿晗'}如何使用 One-hot 编码器:
import joblib
from tensorflow.keras.preprocessing.text import Tokenizer
vocabs = {"周杰伦", "陈奕迅", "王力宏", "李宗盛", "吴亦凡", "鹿晗"}
# 加载已保存的词汇映射器Tokenizer joblib.load(mypath)
mypath = './mytokenizer'
mytokenizer = joblib.load(mypath)
# 编码token为"李宗盛" 查询单词idx 赋值 zero_list,生成onehot
token = "李宗盛"
zero_list = [0] * len(vocabs)
idx = mytokenizer.word_index[token] - 1
zero_list[idx] = 1
print(token, '的onehot编码是', zero_list)
# 李宗盛 的onehot编码是 [0, 0, 0, 0, 1, 0]one-hot编码的优劣势:
- 优势:操作简单,容易理解。
- 劣势:完全割裂了词与词之间的联系,而且在大语料集下,每个向量的长度过大,占据大量内存。
正因为 one-hot 编码明显的劣势,这种编码方式被应用的地方越来越少,取而代之的是接下来我们要学习的稠密向量的表示方法 word2vec 和 word embedding。
Word2Vec
word2vec 是一种流行的将词汇表示成向量的无监督训练方法,该过程将构建神经网络模型,将网络参数作为词汇的向量表示,它包含 CBOW 和 skipgram 两种训练模式。
CBOW 模式
CBOW,全称 Continuous bag of words,给定一段用于训练的文本语料,再选定某段长度(窗口)作为研究对象,使用上下文词汇预测目标词汇。

图中窗口大小为 9,使用前后 4 个词汇对目标词汇进行预测。
CBOW 本质是两层神经网络:context words → embedding → 平均/求和 → 线性层 → softmax → 预测词。
首先,每个上下文词 → one-hot 向量,然后 Embedding 层做查表,得到词向量 x1,⋯,xn,它们是输入词的 n 个特征,然后窗口中的数个词每个的词向量取平均。输出层通过 Softmax 输出词表中每个词出现的概率,得到预测词。
CBOW 模式下的 word2vec 过程说明:
假设我们给定的训练语料只有一句话:Hope can set you free (愿你自由成长),窗口大小为 3,因此模型的第一个训练样本来自 Hope can set,因为是 CBOW 模式,所以将使用 Hope 和 set 作为输入,can 作为输出,在模型训练时,Hope,can,set 等词汇都使用它们的 one-hot 编码。如图所示:每个 one-hot 编码的单词与各自的变换矩阵(即参数矩阵 3x5, 这里的 3 是指最后得到的词向量维度)相乘之后再相加,得到上下文表示矩阵(3x1)。

接着,将上下文表示矩阵与变换矩阵(参数矩阵 5x3,所有的变换矩阵共享参数)相乘,得到 5x1 的结果矩阵,它将与我们真正的目标矩阵即 can 的 one-hot 编码矩阵(5x1)进行损失的计算,然后更新网络参数完成一次模型迭代。

最后窗口按序向后移动,重新更新参数,直到所有语料被遍历完成,得到最终的变换矩阵 (3x5),这个变换矩阵与每个词汇的 one-hot 编码(5x1)相乘,得到的 3x1 的矩阵就是该词汇的 word2vec 张量表示。
Skipgram 模式
Skipgram 给定一段用于训练的文本语料,再选定某段长度(窗口)作为研究对象,使用目标词汇预测上下文词汇。

Skipgram 也是一段神经网络,组成是中心词 → embedding → 线性层 → softmax → 多个上下文词概率。
首先中心词输入,得到 one-hot 向量,进入 embedding,得到词向量,输出层对文本做分类,得到周围每个词的概率。
分析:图中窗口大小为 9,使用目标词汇对前后四个词汇进行预测。
假设我们给定的训练语料只有一句话:Hope can set you free (愿你自由成长),窗口大小为3,因此模型的第一个训练样本来自 Hope can set,因为是 skipgram 模式,所以将使用 can 作为输入,Hope 和 set 作为输出,在模型训练时,Hope,can,set 等词汇都使用它们的 one-hot 编码。如图所示:将 can 的 one-hot 编码与变换矩阵(即参数矩阵 3x5,这里的 3 是指最后得到的词向量维度)相乘,得到目标词汇表示矩阵(3x1)。
接着,将目标词汇表示矩阵与多个变换矩阵(参数矩阵 5x3)相乘,得到多个 5x1 的结果矩阵,它将与我们 Hope 和 set 对应的 one-hot 编码矩阵(5x1)进行损失的计算,然后更新网络参数完成一次模型迭代。

最后窗口按序向后移动,重新更新参数,直到所有语料被遍历完成,得到最终的变换矩阵即参数矩阵(3x5),这个变换矩阵与每个词汇的 one-hot 编码(5x1)相乘,得到的 3x1 的矩阵就是该词汇的 word2vec 张量表示。
Word2Vec 的训练和使用
数据来源:http://mattmahoney.net/dc/enwik9.zip,它是英语维基百科的部分网页信息,大小在 300M 左右。
查看原始数据:
$ head -10 data/enwik9
<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.3/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.3/ http://www.mediawiki.org/xml/export-0.3.xsd" version="0.3" xml:lang="en">
<siteinfo>
<sitename>Wikipedia</sitename>
<base>http://en.wikipedia.org/wiki/Main_Page</base>
<generator>MediaWiki 1.6alpha</generator>
<case>first-letter</case>
<namespaces>
<namespace key="-2">Media</namespace>
<namespace key="-1">Special</namespace>
<namespace key="0" />需要做一下文本预处理,通过正则表达式去掉输出的 HTML 和 XML 标签,得到纯英文:
anarchism originated as a term of abuse first used against early working class接下来开始训练,这里我们用到 facebook 开源的 fasttext,使用 pip 安装:
pip install fasttext本人的 Python 为 3.13,操作系统为 Windows,所以 fasttext 没有安装成功。
# 导入fasttext
import fasttext
# 使用train_unsupervised(无监督训练方法)训练词向量
mymodel = fasttext.train_unsupervised('./data/fil9')
print('训练词向量 ok')
# save_model()保存已经训练好词向量
mymodel.save_model("./data/fil9.bin")
print('保存词向量 ok')
# 模型加载
mymodel = fasttext.load_model('./data/fil9.bin')
print('加载词向量 ok')有效训练词汇量为124M, 共218316个单词
Read 124M words
Number of words: 218316
Number of labels: 0
Progress: 100.0% words/sec/thread: 53996 lr: 0.000000 loss: 0.734999 ETA: 0h 0m查看单词对应的词向量:
# 通过get_word_vector方法来获得指定词汇的词向量, 默认词向量训练出来是1个单词100特征
mymodel = fasttext.load_model('./data/fil9.bin')
myvector = mymodel.get_word_vector('the')
print('myvector->', type(myvector), myvector.shape, myvector)
# 运行效果如下:
# array([-0.03087516, 0.09221972, 0.17660329, 0.17308897, 0.12863874,
# 0.13912526, -0.09851588, 0.00739991, 0.37038437, -0.00845221,
# ...
# -0.21184735, -0.05048715, -0.34571868, 0.23765688, 0.23726143],
# dtype=float32)模型效果检验:
# 检查单词向量质量的一种简单方法就是查看其邻近单词, 通过我们主观来判断这些邻近单词是否与目标单词相关来粗略评定模型效果好坏.
# 查找"运动"的邻近单词, 我们可以发现"体育网", "运动汽车", "运动服"等.
>>> model.get_nearest_neighbors('sports')
[(0.8414610624313354, 'sportsnet'), (0.8134572505950928, 'sport'), (0.8100415468215942, 'sportscars'), (0.8021156787872314, 'sportsground'), (0.7889881134033203, 'sportswomen'), (0.7863013744354248, 'sportsplex'), (0.7786710262298584, 'sporty'), (0.7696356177330017, 'sportscar'), (0.7619683146476746, 'sportswear'), (0.7600985765457153, 'sportin')]
# 查找"音乐"的邻近单词, 我们可以发现与音乐有关的词汇.
>>> model.get_nearest_neighbors('music')
[(0.8908010125160217, 'emusic'), (0.8464668393135071, 'musicmoz'), (0.8444250822067261, 'musics'), (0.8113634586334229, 'allmusic'), (0.8106718063354492, 'musices'), (0.8049437999725342, 'musicam'), (0.8004694581031799, 'musicom'), (0.7952923774719238, 'muchmusic'), (0.7852965593338013, 'musicweb'), (0.7767147421836853, 'musico')]
# 查找"小狗"的邻近单词, 我们可以发现与小狗有关的词汇.
>>> model.get_nearest_neighbors('dog')
[(0.8456876873970032, 'catdog'), (0.7480780482292175, 'dogcow'), (0.7289096117019653, 'sleddog'), (0.7269964218139648, 'hotdog'), (0.7114801406860352, 'sheepdog'), (0.6947550773620605, 'dogo'), (0.6897546648979187, 'bodog'), (0.6621081829071045, 'maddog'), (0.6605004072189331, 'dogs'), (0.6398137211799622, 'dogpile')]Word2Vec 超参数设定
fasttext.train_unsupervised('data/fil9', "cbow", dim=300, epoch=1, lr=0.1, thread=8)train_unsupervised 可以指定这五个参数:
'data/fil9':训练语料文件路径,要求是纯文本。cbow:也可以是skipgram,代表选用的模型类别。dim=300:词向量维度。epoch=1:训练轮数,越大,模型学习越充分,但是更容易过拟合。lr=0.1:学习率,控制参数更新步长。thread=8:训练使用的线程数。
Word Embedding
它通过一定的方式将词汇映射到指定维度的空间。
广义的 word embedding 包括所有密集词汇向量的表示方法,如之前学习的 word2vec, 即可认为是 word embedding 的一种。
狭义的 word embedding 是指在神经网络中加入的 embedding 层,对整个网络进行训练的同时产生的 embedding 矩阵(embedding 层的参数), 这个 embedding 矩阵就是训练过程中所有输入词汇的向量表示组成的矩阵。
这个 Embedding 不是 RNN 或 Word2Vec 的两种类型种的词嵌入层,是一种通用机制。它本质是一个 VxD 的矩阵,是一个线性层(神经网络的最基本结构),其中 V 是词表大小,D 是向量维度。Embedding 由一个输入编码和矩阵 E 组成,输入编码(one-hot)负责定位“第几个词”,矩阵 E 提供向量内容。E 就是一堆浮点数,所以也需要初始化,也是通过梯度下降不断调整的,而更新任务来源于 CBOW、Skipgram 或下游任务。所以 embedding = 初始化随机值 + 训练过程中累积的梯度信息。
Python代码:
demo.py
import jieba
import tensorflow as tf
import torch.nn as nn
sentence1 = "将一段文本使用张量进行表示,其中一般将词汇表示成向量,称作词向量,再由各个词向量按顺序组成矩阵形成文本表示"
sentence2 = "我爱自然语言处理"
sentences = [sentence1, sentence2]
word_list = []
for sentence in sentences:
word_list.append(jieba.lcut(sentence))
print(f"分词结果:{word_list}")
# 构建词汇表进行文本数值化(转换为词向量)
tokenizer = tf.keras.preprocessing.text.Tokenizer()
tokenizer.fit_on_texts(word_list)
# 查看词映射索引字典
print(f"词映射索引字典:{tokenizer.word_index}")
# 获取去重后的所有词汇列表
token_list = tokenizer.word_index.values()
seq2id = tokenizer.texts_to_sequences(word_list)
print(f"文本转序列:{seq2id}")
# 创建词嵌入层,参数1:词汇表大小,即唯一的词的个数,参数2:词向量维度大小
embedding = nn.Embedding(num_embeddings=len(token_list), embedding_dim=8)
# 查看词嵌入层的权重参数
print(f"词嵌入层的权重参数:{embedding.weight.data}")
print(f"查看词嵌入层的权重参数的形状:{embedding.weight.data.shape}")运行结果.txt
分词结果:[['将', '一段', '文本', '使用', '张量', '进行', '表示', ',', '其中', '一般', '将', '词汇', '表示', '成', '向量', ',', '称作', '词', '向量', ',', '再', '由', '各个', '词', '向量', '按', '顺序', '组成', '矩阵', '形成', '文本', '表示'], ['我', '爱', '自然语言', '处理']]
词映射索引字典:{'表示': 1, ',': 2, '向量': 3, '将': 4, '文本': 5, '词': 6, '一段': 7, '使用': 8, '张量': 9, '进行': 10, '其中': 11, '一般': 12, '词汇': 13, '成': 14, '称作': 15, '再': 16, '由': 17, '各个': 18, '按': 19, '顺序': 20, '组成': 21, '矩阵': 22, '形成': 23, '我': 24, '爱': 25, '自然语言': 26, '处理': 27}
文本转序列:[[4, 7, 5, 8, 9, 10, 1, 2, 11, 12, 4, 13, 1, 14, 3, 2, 15, 6, 3, 2, 16, 17, 18, 6, 3, 19, 20, 21, 22, 23, 5, 1], [24, 25, 26, 27]]
词嵌入层的权重参数:tensor([[-1.0837e+00, -2.3960e+00, -7.3111e-01, -1.5198e+00, -1.8328e-01,
-1.4065e+00, 9.4990e-02, -1.3371e+00],
...
[-1.2370e+00, 1.2488e+00, 1.1617e+00, -5.6203e-01, -1.1735e-01,
-1.4185e+00, 2.4771e-01, -7.8150e-01]])
查看词嵌入层的权重参数的形状:torch.Size([27, 8])
单词: 表示, 词向量: [ 0.2513009 0.6627234 1.1603111 1.0070332 1.2764151 0.44307926
-0.9136338 0.4520443 ]
...CBOW 和 Skipgram 的另一种解释
CBOW,连续词袋模型,全称为 Continuous Bag of Words,是 Word2Vec 的一种实现方法,会根据上下文预测目标词汇。
它们的目标是先迭代出词向量字典(嵌入矩阵)。
CBOW 实际上是一个神经网络,会接收上下文词语,将它们转换为最有可能的目标词。它的 Embeddings 层,是一个 N×V 的矩阵,其中 N 为词表大小,V 为词向量维度。该矩阵就是最终需要学习得到的词向量矩阵(嵌入矩阵),作用是将输入词映射为稠密向量表示。
输入词首先会通过 One-Hot 编码转换为一个 1×N 的向量,其中只有对应词位置为 1,其余为 0。然后将该向量与嵌入矩阵相乘,得到该词的词向量表示,为一个 1×V 的向量。本质上等价于从 Embedding 矩阵中“按行查表”,即直接取出对应词的向量。
如果某个词的上下文包含多个词语,则这些词分别通过 Embedding 层得到多个向量,然后进行求和或平均(通常使用平均),得到上下文的语义表示向量。
CBOW 的输出层是一个线性层(通常不使用激活函数),权重矩阵维度为 V×N,相当于将隐层向量映射回词表空间。该线性层输出一个 1×N 的向量,再通过 softmax 归一化,得到目标词在词表上的概率分布,概率最大的词即为预测结果。
Skip-gram,跳字模型,也是 Word2Vec 的一种实现方法,会根据目标词预测上下文。
Skip-gram 在训练过程中,会调整词向量,使目标词的词向量与上下文词的词向量尽可能接近,同时使目标词与非上下文词的词向量尽可能远离,从而学习语义空间结构。
Skip-gram 也是一个神经网络结构,通常可以理解为包含一层 Embedding 层 + 输出层(softmax 分类层)。
输入词经过 Embedding 层后得到词向量表示,然后通过输出层分别预测窗口范围内的多个上下文词。输出结果是一个对词表大小为 N 的概率分布,表示词表中每个词作为上下文词的可能性。
由于一个中心词需要预测多个上下文词,因此 Skip-gram 通常会产生多个训练样本(中心词 → 每个上下文词分别构成一组监督信号)。
文本数据分析
文本数据分析能够有效帮助我们理解数据语料,快速检查出语料可能存在的问题,并指导之后模型训练过程中一些超参数的选择。
常用的几种文本数据分析方法:
- 标签数量分布
- 句子长度分布
- 词频统计与关键词词云
我们将基于真实的中文酒店评论语料来讲解常用的几种文本数据分析方法。
中文酒店评论语料:
- 属于二分类的中文情感分析语料,该语料存放在“./cn_data”目录下。
- 其中 train.tsv 代表训练集,dev.tsv代表验证集,二者数据样式相同。
数据样式:
sentence label
早餐不好,服务不到位,晚餐无西餐,早餐晚餐相同,房间条件不好,餐厅不分吸烟区.房间不分有无烟房. 0
去的时候 ,酒店大厅和餐厅在装修,感觉大厅有点挤.由于餐厅装修本来该享受的早饭,也没有享受(他们是8点开始每个房间送,但是我时间来不及了)不过前台服务员态度好! 1
有很长时间没有在西藏大厦住了,以前去北京在这里住的较多。这次住进来发现换了液晶电视,但网络不是很好,他们自己说是收费的原因造成的。其它还好。 1
非常好的地理位置,住的是豪华海景房,打开窗户就可以看见栈桥和海景。记得很早以前也住过,现在重新装修了。总的来说比较满意,以后还会住 1
交通很方便,房间小了一点,但是干净整洁,很有香港的特色,性价比较高,推荐一下哦 1
酒店的装修比较陈旧,房间的隔音,主要是卫生间的隔音非常差,只能算是一般的 0
酒店有点旧,房间比较小,但酒店的位子不错,就在海边,可以直接去游泳。8楼的海景打开窗户就是海。如果想住在热闹的地带,这里不是一个很好的选择,不过威海城市真的比较小,打车还是相当便宜的。晚上酒店门口出租车比较少。 1
位置很好,走路到文庙、清凉寺5分钟都用不了,周边公交车很多很方便,就是出租车不太爱去(老城区路窄爱堵车),因为是老宾馆所以设施要陈旧些, 1
酒店设备一般,套房里卧室的不能上网,要到客厅去。 0train.tsv 中的数据内容共分为 2 列,第一列数据代表具有感情色彩的评论文本;第二列数据, 0或1,代表每条文本数据是积极或者消极的评论,0代表消极,1代表积极。
代码示例:
train_data = pd.read_csv('./data/train.tsv', sep='\t')
dev_data = pd.read_csv('./data/dev.tsv', sep='\t')
# 查看 0 1 分组的数量
sns.countplot(x='label', data=train_data, hue='label', legend=False) # 统计训练集 0(负) 和 1(正) 的分组数量
sns.countplot(x='label', data=dev_data, hue='label', legend=False) # 统计训练集 0(负) 和 1(正) 的分组数量
# 查看训练集的句子数量分布
train_data['sentence_length'] = list(map(lambda x: len(x), train_data['sentence']))
sns.countplot(x='sentence_length', data=train_data)
# 查看正负样本散点分布
train_data['sentence_length'] = list(map(lambda x: len(x), train_data['sentence']))
dev_data['sentence_length'] = list(map(lambda x: len(x), dev_data['sentence']))
sns.stripplot(x='label', y='sentence_length', data=train_data)
sns.stripplot(x='label', y='sentence_length', data=dev_data)
# 词频统计
train_vocab = set(chain(*map(lambda x: jieba.lcut(x), train_data['sentence'])))
dev_vocab = set(chain(*map(lambda x: jieba.lcut(x), dev_data['sentence'])))
print(f'训练集不同词汇总数: {len(train_vocab)}')
print(f'测试集不同词汇总数: {len(dev_vocab)}')文本特征增强
N-gram
给定一段文本序列,其中 n 个词或字的相邻共现特征即 n-gram 特征,常用的 n-gram 特征是 bi-gram 和 tri-gram 特征,分别对应 n 为 2 和 3。
假设给定分词列表:["是谁", "敲动", "我心"],对应的数值映射列表为:[1, 34, 21],我们可以认为数值映射列表中的每个数字是词汇特征。除此之外,我们还可以把“是谁”和“敲动”两个词共同出现且相邻也作为一种特征加入到序列列表中。假设1000就代表“是谁”和“敲动”共同出现且相邻,此时数值映射列表就变成了包含 2-gram 特征的特征列表:[1, 34, 21, 1000],这里的“是谁”和“敲动”共同出现且相邻就是 bi-gram 特征中的一个,“敲动”和“我心”也是共现且相邻的两个词汇,因此它们也是 bi-gram 特征。假设 1001 代表“敲动”和“我心”共同出现且相邻,那么,最后原始的数值映射列表 [1, 34, 21] 添加了 bi-gram 特征之后就变成了 [1, 34, 21, 1000, 1001]。
提取 N-gram 特征:
# 一般n-gram中的n取2或者3, 这里取2为例
ngram_range = 2
def create_ngram_set(input_list):
"""
description: 从数值列表中提取所有的n-gram特征
:param input_list: 输入的数值列表, 可以看作是词汇映射后的列表,
里面每个数字的取值范围为[1, 25000]
:return: n-gram特征组成的集合
eg:
>>> create_ngram_set([1, 3, 2, 1, 5, 3])
{(3, 2), (1, 3), (2, 1), (1, 5), (5, 3)}
"""
return set(zip(*[input_list[i:] for i in range(ngram_range)]))
input_list = [1, 3, 2, 1, 5, 3]
res = create_ngram_set(input_list)
print(res)
# {(3, 2), (1, 3), (2, 1), (1, 5), (5, 3)}文本长度规范
一般模型的输入需要等尺寸大小的矩阵,因此在进入模型前需要对每条文本数值映射后的长度进行规范,此时将根据句子长度分布分析出覆盖绝大多数文本的合理长度,对超长文本进行截断,对不足文本进行补齐(一般使用数字 0),这个过程就是文本长度规范。
实现:
from tensorflow.keras.preprocessing import sequence
# cutlen根据数据分析中句子长度分布,覆盖90%左右语料的最短长度.
# 这里假定cutlen为10
cutlen = 10
def padding(x_train):
"""
description: 对输入文本张量进行长度规范
:param x_train: 文本的张量表示, 形如: [[1, 32, 32, 61], [2, 54, 21, 7, 19]]
:return: 进行截断补齐后的文本张量表示
"""
# 使用sequence.pad_sequences即可完成
return sequence.pad_sequences(x_train, cutlen)
# 假定x_train里面有两条文本, 一条长度大于10, 一天小于10
x_train = [[1, 23, 5, 32, 55, 63, 2, 21, 78, 32, 23, 1],
[2, 32, 1, 23, 1]]
res = padding(x_train)
print(res)
# [[ 5 32 55 63 2 21 78 32 23 1]
# [ 0 0 0 0 0 2 32 1 23 1]]文本数据增强
通过如 Google Translate 等机器翻译工具,将原文本翻译成另一种语言再翻译回原语言,从而生成语义相同但表达不同的新样本。
- 扩大数据集规模:标注数据稀缺时,低成本生成更多训练样本
- 提升模型泛化能力:通过多样表达方式,减少模型对固定句式的依赖
- 增强鲁棒性:让模型更能适应不同表述、同义改写等情况
- 缓解过拟合:引入“轻度扰动”的新数据,使模型不过度记忆原始数据
- 提升低资源语言任务效果:在数据较少的语言或领域中尤其有效