提前说明:
1. 以下内容结合K同学啊和gpt自学完成
2. Pytorch的使用需要下载VB
代码操练(录入训练数据——形成词汇表——检验)
单文本版
# 导入必要的库
from torchtext.vocab import build_vocab_from_iterator # 用于构建词汇表
from collections import Counter # 用于词频统计(在构建词汇表时有用)
from torchtext.data.utils import get_tokenizer # 获取分词器
import jieba # 中文分词库
import re # 正则表达式库
import torch # PyTorch 库
# 示例文本数据
data = [
"我是K同学啊!", # 一句话
"我是一个深度学习博主,", # 另一句话
"这是我的365天深度学习训练营教案", # 第三句话
"你可以通过百度、微信搜索关键字【K同学啊】找到我" # 第四句话
]
# 中文分词方法,使用 jieba 分词工具
tokenizer = jieba.lcut
# 如果有自定义词典,可以通过此方法加载
# jieba.load_userdict("my_dict.txt")
# 去除标点符号的函数
def remove_punctuation(text):
"""
使用正则表达式去除文本中的标点符号。
"""
return re.sub(r'[^\w\s]', '', text) # 匹配所有非字母、数字或空格的字符,替换为空
# 假设我们有一个简单的停用词表,包含一些无意义的常用词
stopwords = set([
"的", "这", "是" # 停用词示例
])
# 去除停用词的函数
def remove_stopwords(words):
"""
从分词后的列表中去除停用词。
"""
return [word for word in words if word not in stopwords] # 保留不在停用词表中的词
# 生成分词迭代器,用于构建词汇表
def yield_tokens(data):
"""
对文本数据进行分词并去除停用词和标点符号,生成词汇列表。
"""
for text in data:
# 使用 jieba 对文本进行分词
words = tokenizer(text)
# 去除停用词
words = remove_stopwords(words)
# 去除标点符号
words = [remove_punctuation(word) for word in words]
# 生成一个词汇列表,供词汇表构建使用
yield words
# 使用 build_vocab_from_iterator 来构建词汇表
# 该函数会根据分词后的数据构建一个字典,记录每个词的频次和索引
vocab = build_vocab_from_iterator(yield_tokens(data), specials=["<unk>"])
# 将词汇表中的未知词(未出现在训练数据中的词)映射到 <unk> 索引
vocab.set_default_index(vocab["<unk>"])
# 打印词汇表的大小和词汇表内部的映射关系(即词和索引的对应关系)
print("词典大小:", len(vocab)) # 输出词汇表中不同词汇的数量
print("词典内部映射:", vocab.get_stoi()) # 输出词到索引的映射
# 示例文本
text = "这是我的365天深度学习训练营教案"
# 使用 jieba 对示例文本进行分词
words = remove_stopwords(jieba.lcut(text)) # 去除停用词后的词汇列表
print("\n")
# 打印分词后的文本
print("jieba分词后的文本:", jieba.lcut(text)) # 输出分词结果
# 打印去除停用词后的文本
print("去除停用词后的文本:", words) # 输出去除停用词后的结果
# 将文本中的词转换成数字索引
print("数字化后的文本:", [vocab[word] for word in words]) # 输出数字化后的词汇索引
结果展示:
%runfile E:/DL/N2_construct_a_dict.py --wdir D:\App\Anaconda\envs\sharlin\Lib\site-packages\torchtext\vocab\__init__.py:4: UserWarning: /!\ IMPORTANT WARNING ABOUT TORCHTEXT STATUS /!\ Torchtext is deprecated and the last released version will be 0.18 (this one). You can silence this warning by calling the following at the beginnign of your scripts: import torchtext; torchtext.disable_torchtext_deprecation_warning() warnings.warn(torchtext._TORCHTEXT_DEPRECATION_MSG) D:\App\Anaconda\envs\sharlin\Lib\site-packages\torchtext\utils.py:4: UserWarning: /!\ IMPORTANT WARNING ABOUT TORCHTEXT STATUS /!\ Torchtext is deprecated and the last released version will be 0.18 (this one). You can silence this warning by calling the following at the beginnign of your scripts: import torchtext; torchtext.disable_torchtext_deprecation_warning() warnings.warn(torchtext._TORCHTEXT_DEPRECATION_MSG)
词典大小: 22
词典内部映射: {'<unk>': 0, '': 1, '可以': 13, '我': 2, 'K': 3, '深度': 7, '微信': 15, '学习': 6, '同学': 4, '啊': 5, '365': 8, '一个': 9, '你': 10, '关键字': 11, '博主': 12, '天': 14, '找到': 16, '搜索': 17, '教案': 18, '百度': 19, '训练营': 20, '通过': 21}
jieba分词后的文本: ['这', '是', '我', '的', '365', '天', '深度', '学习', '训练营', '教案']
去除停用词后的文本: ['我', '365', '天', '深度', '学习', '训练营', '教案']
数字化后的文本: [2, 8, 14, 7, 6, 20, 18] D:\App\Anaconda\envs\sharlin\Lib\site-packages\torchtext\data\__init__.py:4: UserWarning: /!\ IMPORTANT WARNING ABOUT TORCHTEXT STATUS /!\ Torchtext is deprecated and the last released version will be 0.18 (this one). You can silence this warning by calling the following at the beginnign of your scripts: import torchtext; torchtext.disable_torchtext_deprecation_warning() warnings.warn(torchtext._TORCHTEXT_DEPRECATION_MSG)
从输出信息来看,torchtext 库出现了一个警告,说明它已经被标记为 “已弃用(deprecated)”,并且最后一个版本是 0.18。这意味着未来版本可能不会继续支持 torchtext
,而 PyTorch 团队推荐用户逐步迁移到新的文本处理工具,如 torchdata
或 transformers
等。
不过,警告信息不会影响当前代码的运行,只是提醒你该库将在将来不再更新。
解决警告:
如果希望消除这个警告,可以在代码开头加上以下语句来关闭它:
import torchtext
torchtext.disable_torchtext_deprecation_warning()
其他输出内容分析:
-
词典大小:构建的词汇表大小是
22
,包含了22
个不同的词汇。 -
词汇表映射:
<unk>
对应索引0
,以及其他词汇(如我
,K
,深度
等)也都分配了唯一的索引。 -
jieba 分词后的文本:
['这', '是', '我', '的', '365', '天', '深度', '学习', '训练营', '教案']
,这是原始的分词结果。 -
去除停用词后的文本:
['我', '365', '天', '深度', '学习', '训练营', '教案']
,去掉了常见的停用词(如这
,是
,的
)。 -
数字化后的文本:
[2, 8, 14, 7, 6, 20, 18]
,这些是每个词汇在词汇表中的索引。
建议:
-
关注弃用警告:
-
如果计划继续使用
torchtext
,这不影响当前的代码执行,但长远来看,建议关注 PyTorch 的未来版本更新,看看是否有其他更好的替代方案(比如torchdata
或transformers
)。
-
-
代码继续执行:
-
代码在去除停用词、分词和数字化后执行正常,输出结果也符合预期。这表明大部分功能正常运行。
-
录入文本版
#########################################导入文件版
from torchtext.vocab import build_vocab_from_iterator # 用于构建词汇表
from collections import Counter # 用于词频统计(在构建词汇表时有用)
from torchtext.data.utils import get_tokenizer # 获取分词器
import jieba # 中文分词库
import re # 正则表达式库
import torch # PyTorch 库
#torchtext.disable_torchtext_deprecation_warning() 这条可以取消警告
# 读取文件中的文本数据
def load_texts_from_file(file_path):
with open(file_path, 'r', encoding='utf-8') as f:
texts = [line.strip() for line in f.readlines()]
return texts
# 文件路径
file_path = r"C:\Users\King\Desktop\texts\任务文件.txt" # 替换成你的文件路径
# 加载文本
data2 = load_texts_from_file(file_path)
print(data2)
# 去除标点符号的函数
def remove_punctuation(text):
"""
使用正则表达式去除文本中的标点符号。
"""
return re.sub(r'[^\w\s]', '', text) # 匹配所有非字母、数字或空格的字符,替换为空
# 假设我们有一个简单的停用词表,包含一些无意义的常用词
stopwords = set([
"的", "这", "是" # 停用词示例
])
# 去除停用词的函数
def remove_stopwords(words):
"""
从分词后的列表中去除停用词。
"""
return [word for word in words if word not in stopwords] # 保留不在停用词表中的词
# 生成分词迭代器,用于构建词汇表
def yield_tokens(data2):
"""
对文本数据进行分词并去除停用词和标点符号,生成词汇列表。
"""
for text in data2:
# 使用 jieba 对文本进行分词
words = jieba.lcut(text)
# 去除停用词
words = remove_stopwords(words)
# 去除标点符号
words = [remove_punctuation(word) for word in words]
# 生成一个词汇列表,供词汇表构建使用
yield words
# 使用 build_vocab_from_iterator 来构建词汇表
# 该函数会根据分词后的数据构建一个字典,记录每个词的频次和索引
vocab = build_vocab_from_iterator(yield_tokens(data2), specials=["<unk>"]) #当你调用 build_vocab_from_iterator 函数时,specials=["<unk>"] 这一部分指定了你要在词汇表中包含的特殊标记。在这种情况下,特殊标记是 <unk>。
#build_vocab_from_iterator 会根据文本数据构建词汇表,并自动将 <unk> 作为一个特殊标记添加到词汇表中,并为其分配一个索引(通常是 0)。
# 将词汇表中的未知词(未出现在训练数据中的词)映射到 <unk> 索引
vocab.set_default_index(vocab["<unk>"])
# 打印词汇表的大小和词汇表内部的映射关系(即词和索引的对应关系)
print("词典大小:", len(vocab)) # 输出词汇表中不同词汇的数量
print("词典内部映射:", vocab.get_stoi()) # 输出词到索引的映射
#print(vocab.get_stoi()) # 打印字符串到索引的映射
#print(vocab.get_itos()) # 打印索引到字符串的映射
#打印的是 训练数据(data2)中所有唯一词汇到索引的映射,即词汇表。这个词汇表是根据训练数据构建的,它包含了训练数据中 所有出现过的唯一词汇 和这些词汇对应的 索引。
#这个映射关系基于 训练数据(data2) 中的分词结果创建的。所以,它不直接打印出训练集本身,而是打印出训练集中的词汇及其对应的索引
# 处理测试数据
test_text = ["我","比较", "倾向于","学习", "深度", "学习", "模型"]
# 映射结果
mapped_indices = [vocab[WORD] for WORD in test_text]
print(mapped_indices)
word = "学习"
if word in vocab.get_stoi():
print(f"'{word}' 在词汇表中")
else:
print(f"'{word}' 不在词汇表中")
# if "学习" in vocab.get_stoi():
# print(f"'学习' 在词汇表中")
# else:
# print(f"'学习' 不在词汇表中")
['比较直观的编码方式是采用上面提到的字典序列。', '例如,对于一个有三个类别的问题,可以用1、2和3分别表示这三个类别。', '但是,这种编码方式存在一个问题,就是模型可能会错误地认为不同类别之间存在一些顺序或距离关系', '而实际上这些关系可能是不存在的或者不具有实际意义的,为了避免这种问题,引入了one-hot编码(也称独热编码)。', 'one-hot编码的基本思想是将每个类别映射到一个向量,其中只有一个元素的值为1,其余元素的值为0。', '这样,每个类别之间就是相互独立的,不存在顺序或距离关系。', '例如,对于三个类别的情况,可以使用如下的one-hot编码:', '这是K同学啊的“365天深度学习训练营”教案内容']
词典大小: 90
词典内部映射: {'<unk>': 0, '实际上': 59, '编码方式': 24, '如下': 56, '元素': 17, '': 1, 'hot': 6, '学习': 58, '分别': 46, '存在': 4, '地': 53, '类别': 2, 'one': 7, '0': 28, '一个': 3, '了': 38, '可能': 19, '只有': 48, '编码': 5, '实际意义': 60, '三个': 8, '不': 9, '值': 16, '啊': 52, '不同': 35, '关系': 10, '问题': 11, '1': 12, '之间': 14, '为': 13, '例如': 15, '其中': 42, '可以': 18, '对于': 20, '就是': 21, '但是': 40, '或': 22, '每个': 23, '序列': 62, '365': 31, '为了': 36, '距离': 25, '这种': 26, '顺序': 27, '2': 29, '将': 61, '3': 30, '一些': 33, 'K': 32, '上面': 34, '也': 37, '会': 39, '使用': 41, '同学': 49, '其余': 43, '具有': 44, '避免': 87, '内容': 45, '到': 47, '向量': 50, '和': 51, '基本': 54, '天': 55, '字典': 57, '引入': 63, '思想': 64, '情况': 65, '或者': 66, '比较': 72, '提到': 67, '教案': 68, '映射': 69, '有': 70, '模型': 71, '深度': 73, '独热': 74, '独立': 75, '用': 76, '直观': 77, '相互': 78, '称': 79, '而': 80, '表示': 81, '认为': 82, '训练营': 83, '这些': 84, '这是': 85, '这样': 86, '采用': 88, '错误': 89}
[0, 72, 0, 58, 73, 58, 71]
'学习' 在词汇表中
Unk到底什么用?
print("词典内部映射:", vocab.get_stoi())
打印的是 训练数据(data2
)中所有唯一词汇到索引的映射,即词汇表。这个词汇表是根据训练数据构建的,它包含了训练数据中 所有出现过的唯一词汇 和这些词汇对应的 索引。
解释:
-
vocab.get_stoi()
返回的是一个字典,表示 词到索引的映射。 -
这个映射关系基于 训练数据(
data2
) 中的分词结果创建的。所以,它不直接打印出训练集本身,而是打印出 训练集中的词汇及其对应的索引。
举个例子:
假设训练数据(data2
)中包含以下几个句子:
The cat is on the mat.
The dog is on the mat.
1. 分词:
使用结巴分词或其他工具分词后,得到的词汇会是
['The', 'cat', 'is', 'on', 'the', 'mat', 'The', 'dog', 'is', 'on', 'the', 'mat']
2. 构建词汇表:
然后通过 build_vocab_from_iterator
生成一个词汇表,其中每个独特词汇都会分配一个索引:
{'<unk>': 0, 'The': 1, 'cat': 2, 'is': 3, 'on': 4, 'the': 5, 'mat': 6, 'dog': 7}
3. 打印词汇表:
当你执行 print("词典内部映射:", vocab.get_stoi())
时,输出的是这样的映射关系:
词典内部映射: {'<unk>': 0, 'The': 1, 'cat': 2, 'is': 3, 'on': 4, 'the': 5, 'mat': 6, 'dog': 7}
详细解释:
在你训练模型时,基于 训练数据(如 data2
)构建了一个词汇表,这个词汇表包含了 训练数据中所有出现过的唯一词汇 以及它们的 索引。但是当你使用这个词汇表处理新的文本时,新的文本可能包含一些在训练数据中没有出现过的词,这些词会被映射为 <unk>
,即 未知词。
如何处理:
所以,词典映射 反映的是训练数据中的词汇,而不是原始文本数据。
-
训练阶段:你使用
data2
来训练模型,并构建词汇表(包括<unk>
)。在训练时,模型会学习如何处理训练数据中的词汇,并为每个词汇分配一个唯一的索引。 -
测试阶段(推理阶段):当你用 新文本 进行预测或推理时,模型会使用训练时得到的词汇表。如果新文本中的某个词没有出现在词汇表中,它会被映射为
<unk>
,即被替换为索引0
(通常为<unk>
的索引)。 -
# 处理测试数据 test_text = ["The", "bird", "is", "on", "the", "mat"] # 映射结果 mapped_indices = [vocab.get(word, vocab["<unk>"]) for word in test_text] # 输出: [1, 0, 3, 4, 5, 6] #bird在原表中不存在,因为unk=0
总结:
-
vocab.get_stoi()
打印的并不是训练集的原始数据,而是 词汇表,即在训练集中的所有唯一词汇和它们的索引映射。 -
这个词汇表是根据你提供的训练数据(
data2
)构建的,包含所有在训练数据中出现的词及它们对应的索引。
拓展
Jieba.cut和jibe.lcut什么区别?
jieba.cut
和 jieba.lcut
都是结巴分词库中的分词函数,它们的主要区别在于返回的结果格式:
1. jieba.cut
:
-
返回类型:返回一个 可迭代对象(generator)。
-
用途:适用于需要按需生成分词结果的场景,例如,当处理非常大的文本数据时,可以逐步处理,避免一次性加载大量数据到内存中。
-
用法:需要使用
list()
或for
循环来获取实际的分词结果。 -
import jieba text = "我喜欢学习深度学习" seg = jieba.cut(text) # 返回的 seg 是一个生成器,可以通过 list() 来转换为列表 print(list(seg)) # ['我', '喜欢', '学习', '深度', '学习']
2.
jieba.lcut
:-
返回类型:直接返回一个 列表(list)。
-
用途:适用于需要直接获取分词列表的场景,代码更加简洁方便,特别是当你需要直接操作分词结果时。
-
用法:直接返回一个列表,可以直接进行操作。
-
import jieba text = "我喜欢学习深度学习" seg = jieba.lcut(text) print(seg) # ['我', '喜欢', '学习', '深度', '学习']
-