小余同学's Bolg - 自然语言处理：基于预训练模型的方法笔记
type: channel
link: https://www.ilfishs.com/tag/%E8%87%AA%E7%84%B6%E8%AF%AD%E8%A8%80%E5%A4%84%E7%90%86-%E5%9F%BA%E4%BA%8E%E9%A2%84%E8%AE%AD%E7%BB%83%E6%A8%A1%E5%9E%8B%E7%9A%84%E6%96%B9%E6%B3%95%E7%AC%94%E8%AE%B0/
items: 《自然语言处理基于预训练模型的方法》- 第5章 静态词向量预训练模型「学习笔记」
items: 《自然语言处理基于预训练模型的方法》- 第4章静态词向量预训练模型「学习笔记」
items: 《自然语言处理基于预训练模型的方法》- 第3章 基础工具集与常用数据集「学习笔记」
items: 《自然语言处理基于预训练模型的方法》- 第2章 自然语言处理基础 「学习笔记」

《自然语言处理基于预训练模型的方法》- 第5章 静态词向量预训练模型「学习笔记」
type: item
date: 2022-06-23T09:10:00+08:00
description: 情感分类实战词表映射Vocabfrom collections import defaultdict # 词表映射 class Vocab: def __init__(self, tokens=None): # 索引对应token的列表 self.idx_to_token = list() # token和索引的词典 self.token_to_idx = dict() if tokens is not None: # 在tokens列表后加标记<unk>来分辨未登录词 if '<unk>' not in tokens: tokens = tokens + ['<unk>'] for token in tokens: self.idx_to_token.append(token) # 下标从0开始 self.token_to_idx[token] = len(self.idx_to_token) - 1 self.unk = self.token_to_idx['<unk>'] @classmethod def build(cls, text, min_freq=1, reserved_tokens=None): # 若查找不存在，返回0 token_freqs = defaultdict(int) for sentence in text: for token in sentence: token_freqs[token] += 1 # 不重复tokens列表 uniq_tokens = ['<unk>'] + (reserved_tokens if reserved_tokens else []) uniq_tokens += [token for token, freq in token_freqs.items() if freq >= min_freq and token != '<unk>'] return cls(uniq_tokens) def __len__(self): # 返回词表的大小，即此表中有多少个互不相同的标记 return len(self.idx_to_token) def __getitem__(self, item): # 查找输入标记对应的索引值，如果该标记不存在，则返回标记<unk>的索引值（0） return self.token_to_idx.get(item, self.unk) def convert_token_to_idx(self, tokens): # 查找一系列输入标记对应的索引值 return [self.token_to_idx[token] for token in tokens] def convert_idx_to_token(self, idxs): # 查找一系列索引值对应的标记 return [self.idx_to_token[idx] for idx in idxs]Embedding层将一个词（或者标记）转换为一个低维、稠密、连续的词向量（也称Embed-ding）是一种基本的词表示方法，通过torch.nn包提供的Embedding层即可实现该功能。创建Embedding对象时，需要提供两个参数，分别是num_embeddings，即词表的大小；以及embedding_dim，即Embedding向量的维度。embedding = nn.Embedding(8,3) # 词表大小为8，Embedding向量维度为3 input = torch.tensor([[0,1,2,1],[4,6,6,7]], dtype=torch.long) # 输入形状为(2,4)的整数张量（相当于两个长度为4的整数序列） # 其中每个整数范围在0-7 output = embedding(input) #调用Embedding对象 print(output) # 输出结果，其中将相同的整数映射为相同的向量融入词向量层的MLPimport torch from torch import nn from torch.nn import functional as F class MLP(nn.Module): def __init__(self, vocab_size, embedding_dim, hidden_dim, num_class): super(MLP, self).__init__() """ 词袋（Bag-Of-Words，BOW）模型解决该问题。 词袋模型指的是在表示序列时，不考虑其中元素的顺序， 而是将其简单地看成是一个集合""" # 词嵌入层 self.embedding = nn.Embedding(vocab_size, embedding…

《自然语言处理基于预训练模型的方法》- 第4章静态词向量预训练模型「学习笔记」
type: item
date: 2022-06-22T19:15:00+08:00
description: 简单的词向量预训练预训练任务基本任务就是根据上下文预测下一时刻词: $P\left(w_{t} \mid w_{1} w_{2} \ldots w_{t-1}\right) $这种监督信号来自于数据自身，因此称为 自监督学习 。前馈神经网络预训练词向量输入层→词向量层→隐含层→输出层训练后，词向量矩阵$ \boldsymbol{E} \in \mathbb{R}^{d \times|\mathbb{V}|}$即为预训练得到的静态词向量。class NGramDataset(Dataset): def __init__(self, corpus, vocab, context_size=2): # context_size:上下文大小为2 self.data = [] self.bos = vocab[BOS_TOKEN] # 句首标记id self.eos = vocab[EOS_TOKEN] # 句尾标记id for sentence in tqdm(corpus, desc="Dataset Construction"): # 插入句首、句尾标记符 sentence = [self.bos] + sentence + [self.eos] # 如句子长度小于预定义的上下文大小，则跳过 if len(sentence) < context_size: continue for i in range(context_size, len(sentence)): # 模型输入：长为context_size的上文 context = sentence[i-context_size:i] # 模型输出：当前词 target = sentence[i] # 每个训练样本由(context,target)构成 self.data.append((context, target)) def __len__(self): return len(self.data) def __getitem__(self, i): return self.data[i] def collate_fn(self, examples): # 从独立样本集合中构建batch的输入输出，并转换为PyTorch张量类型 inputs = torch.tensor([ex[0] for ex in examples], dtype=torch.long) targets = torch.tensor([ex[1] for ex in examples], dtype=torch.long) return (inputs, targets) class FeedForwardNNLM(nn.Module): def __init__(self, vocab_size, embedding_dim, context_size, hidden_dim): super(FeedForwardNNLM, self).__init__() # 词嵌入层 self.embeddings = nn.Embedding(vocab_size, embedding_dim) # 线性变换：词嵌入层->隐含层 self.linear1 = nn.Linear(context_size * embedding_dim, hidden_dim) # 线性变换：隐含层->输出层 self.linear2 = nn.Linear(hidden_dim, vocab_size) # 使用ReLU激活函数 self.activate = F.relu init_weights(self) def forward(self, inputs): embeds = self.embeddings(inputs).view((inputs.shape[0], -1)) hidden = self.activate(self.linear1(embeds)) output = self.linear2(hidden) # 根据输出层（logits）计算概率分布并取对数，以便于计算对数似然 # 这里采用PyTorch库的log_softmax实现 log_probs = F.log_softmax(output, dim=1) return log_probs embedding_dim = 64 context_size = 2 hidden_dim = 128 batch_size = 1024 num_epoch = 10 # 读取文本数据，构建FFNNLM训练数据集（n-grams） corpus, vocab = load_reuters() # print(corpus[0]) # print(corp…

《自然语言处理基于预训练模型的方法》- 第3章 基础工具集与常用数据集「学习笔记」
type: item
date: 2022-06-22T09:00:00+08:00
description: NLTK工具集数据集WordNet : 包含同义词、释义、例句等SentiWordNet : Senti=SentimentWikipediaCommon CrawlPB 级别，7 年爬虫我的妈，使用 Facebook 的 CC-Net 工具进行处理Hugging Face DatasetsWikipedia 数据集使用方法语料处理方法纯文本语料抽取pip install wikiextractor python -m wikiextractor.WikiExtractor python -m wikiextractor.WikiExtractor -h中文简繁体切换我们使用 OpenCC : 甚至可以转换日本新体字等中文字体pip install opencc python convert_t2s.py input_file > output_file数据清洗包括：删除空的成对符号，删除除了 外残留 html 标签，删除不可见控制字符等python wikidata_cleaning.py input_file > output_fileHugging Face Datasets 使用方法数据集获取pip install datasets调用datasetfrom datasets import list_datasets, load_dataset import pprint # dataset loading datasets_list = list_datasets() print(len(datasets_list)) # num_datasets dataset = load_dataset('sst', split='train') # load SST (Stanford Sentiment Treebank) print(len(dataset)) # num_samples pprint(dataset[0]) # {'label':xxx, 'sentence':xxx, 'tokens':xxx, 'tree':xxx}调用 metricsfrom datasets import list_metrics, load_metric # metrics metrics_list = list_metrics() print(len(metrics_list)) # num_metrics accuracy_metric = load_metric('accuracy') results = accuracy_metric.compute(references= [0, 1, 0], predictions= [1, 1, 0]) print(results) # {'accuracy': 0.6666666}

《自然语言处理基于预训练模型的方法》- 第2章 自然语言处理基础 「学习笔记」
type: item
date: 2022-06-21T16:19:00+08:00
description: 书名：自然语言处理：基于预训练模型的方法文本的表示独热编码表示独热编码，就是使用一个词表的大小的向量表示一个词的“语义”，然后第i个词用wi表示。词表中第i个词在第i维上被设置为1，其余维均为0。词的分布式表示由于用独热编码表示词汇导致数据过于稀疏和庞大。因此John RupertFirth于1957年提出了分布式语义假设：词的含义可由其上下文的分布进行表示。我们将以词为单位的其它词语作为上下文，因此创建下列共现频次表。除了词，上下文的选择有很多种方式，而选择不同的上下文得到的词向量表示性质会有所不同。例如，可以使用词在句子中的一个固定窗口内的词作为其上下文，也可以使用所在的文档本身作为上下文。前者得到的词表示将更多地反映词的局部性质：具有相似词法、句法属性的词将会具有相似的向量表示。而后者将更多地反映词代表的主题信息。直接使用与上下文的共现频次作为词的向量表示，至少存在以下三个问题：高频词往往没有什么作用，反而会影响计算机国。如"我" "。"的共现频次很高，实际上可能它们并没有关系但由于共现过，从而产生了较高的相似度。共现频次无法反映词之前的高阶关系。假设词“A”与“B”共现过，“B”与“C”共现过，“C”与“D”共现过，通过共现频次，只能获知“A”与“C”都与“B”共现过，它们之间存在一定的关系，而“A”与“D”这种高阶的关系则无法知晓。仍然存在稀疏性的问题。即向量中仍有大量的值为0解决高频词误导计算结果的问题点互信息由于共矩阵仍然存在稀疏性的问题。即向量中仍有大量的值为0。最直接的想法就是：如果一个词与很多词共现，则降低其权重；反之，如果一个词只与个别词共现，则提高其权重。信息论中的点互信息（Pointwise Mutual Information，PMI）恰好能够做到这一点。对于词w和上下文c，其PMI为：式中，P （w， c）、P （w）、P （c）分别是w与c的共现概率，以及w和c分别出现的概率。可见，通过PMI公式计算，如果w和c的共现概率（与频次正相关）较高，但是w或者c出现的概率也较高（高频词），则最终的PMI值会变小；反之，即便w和c的共现概率不高，但是w或者c出现的概率较低（低频词），则最终的PMI值也可能会比较大。从而较好地解决高频词误导计算结果的问题。当某个词与上下文之间共现次数较低时，可能会得到负的PMI值。考虑到这种情况下的PMI不太稳定（具有较大的方差），在实际应用中通常采用PPMI （Positive PMI）的形式，即：奇异值分解解决共现频次无法反映词之间高阶关系的问题。相关的技术有很多，对共现矩阵M进行奇异值分解。词的分布式表示取得了不错的效果，但是其仍然存在一些问题。当共现矩阵规模较大时，奇异值分解的运行速度非常慢；如果想在原来语料库的基础上增加更多的数据，则需要重新运行奇异值分解算法，代价非常高；分布式表示只能用于表示比较短的单元，如词或短语等，如果待表示的单元比较长，如段落、句子等，由于与其共现的上下文会非常少，则无法获得有效的分布式表示；最后，分布式表示一旦训练完成，则无法修改，也就是说，无法根据具体的任务调整其表示方式。为了解决这些问题，可引入一种新的词表示方式——词嵌入表示。词嵌入表示(Word Embedding)使用一个连续、低维、稠密的向量来表示词，经常直接简称为词向量词向量中的向量值，是随着目标任务的优化过程自动调整的，也就是说，可以将词向量中的向量值看作模型的参数词袋表示所谓词袋表示，就是假设文本中的词语是没有顺序的集合，将文本中的全部词所对应的向量表示（既可以是独热表示，也可以是分布式表示或词向量）相加，即构成了文本的向量表示。如在使用独热表示时，文本向量表示的每一维恰好是相应的词在文本中出现的次数。nlp任务语言模型(统计语言模型）N元语言模型基本任务是在给定词序列w1w2··· wt−1的条件下，对下一时刻t可能出现的词wt的条件概率P （wt|w1w2···wt−1）进行估计。一般地，把w1w2··· wt−1称为wt的历史。随着句子长度增加，w1：i−1出现的次数会越来越少，甚至从未出现过，那么P （wi|w1：i−1）则很可能为0，此时对于概率估计就没有意义了。为了解决该问题，可以假设“下一个词出现的概率只依赖于它前面n−1个词”。马尔可夫假设N-gram ：N 元语言模型马尔可夫假设 : 满足该假设称为：N元语法或文法(gram)模型n=1 的 unigram 独立于历史(之前的序列)，因此语序无关n=2 的 bigram 也被称为一阶马尔可夫链w0 可以是 ，可以是平滑： 解决未登录词 (OOV, Out-Of-Vocabulary, )的零概率问题折扣法 ：高频补低频(频繁出现的N-gram中匀出一部分概率并分配给低频次（含零频次）的N-gram)加…