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

NLP基础1

NLP基础1

深度学习中的NLP的特征输入

1.稠密编码(特征嵌入)

稠密编码(Dense Encoding):指将离散或者高纬的稀疏数据转化为低纬度的连续、密集向量表示

特征嵌入(Feature Embedding)

​ 也称为词嵌入,是稠密编码的一种表现形式,单词映射到一个低维的连续向量空间来表示单词,这些向量通常具有较低的维度,并且每个维度上的值是连续的浮点数。这种表示方法能够捕捉单词之间的语义和句法关系,并且在计算上更高效。

稠密编码的主要特点:

  1. 低维表示
    • 稠密编码将高维的稀疏向量(如 one-hot 编码)映射到低维的密集向量。这样可以减少计算复杂度,并且更好地捕捉词与词之间的关系。
  2. 语义信息
    • 稠密编码能够捕捉单词之间的语义关系。例如,“king” 和 “queen” 在向量空间中可能会非常接近(欧氏距离或余弦相似度),因为它们有相似的语义角色。
  3. 上下文依赖
    • 稠密编码通常是基于上下文生成的。不同的上下文可能会导致同一个单词有不同的向量表示。例如,“bank” 在金融上下文中和在河流上下文中会有不同的向量表示。
  4. 可微学习
    • 嵌入表示通常通过神经网络进行学习,并且通过反向传播算法进行优化。

2.词嵌入算法

2.1 Embedding Layer

嵌入层(Embedding Layer)是深度学习中用于将离散的、高维的稀疏表示(如 one-hot 编码)转换为低维的密集向量表示的一种神经网络层。这种转换能够捕捉到输入数据中的语义信息,并且在计算上更加高效。嵌入层通常用于处理文本数据,例如单词或字符。

嵌入层的主要特点

  1. 低维表示
    • 将高维的稀疏向量(如 one-hot 编码)映射到低维的密集向量空间。例如,一个词汇表大小为 10,000 的 one-hot 编码可以被映射到 100 维的密集向量。
  2. 参数学习
    • 嵌入层的权重是通过训练过程自动学习的。这些权重矩阵存储了每个输入项(如单词)的向量表示。
  3. 语义信息
    • 通过训练,嵌入层能够捕捉到输入项之间的语义关系。例如,在词嵌入中,相似的单词在向量空间中会非常接近。
  4. 灵活性
    • 嵌入层可以应用于多种类型的输入,包括单词、字符、用户 ID 等。

词嵌入层的使用:

nn.Embedding(num_embeddings=10, embedding_dim=4)

参数:

  1. num_embeddings 表示词的数量
  2. embedding_dim 表示用多少维的向量来表示每个词

尝试搭建NNLM语言模型

import torch
import torch.nn as nn
import torch.optim as optim
import jieba
import re


corpus = [
    "床前明月光,疑是地上霜。举头望明月,低头思故乡。",
    "春眠不觉晓,处处闻啼鸟。夜来风雨声,花落知多少。",
    "千山鸟飞绝,万径人踪灭。孤舟蓑笠翁,独钓寒江雪。",
    "白日依山尽,黄河入海流。欲穷千里目,更上一层楼。",
    "茫茫烟水上,日暮阴云飞。孤坐正愁绪,湖南谁捣衣。",
    "国破山河在,城春草木深。感时花溅泪,恨别鸟惊心。",
    "烽火连三月,家书抵万金。白头搔更短,浑欲不胜簪。",
    "人闲桂花落,夜静春山空。月出惊山鸟,时鸣春涧中。"
]


def remove_punctuation(tokens):
    return [token for token in tokens if not re.match(r'[,。!?、;:“”‘’()《》〈〉【】〔〕…—~·]', token)]


sentences = [jieba.lcut(sen) for sen in corpus]
# print(sentences)

sentences = [remove_punctuation(sentence) for sentence in sentences]

# 创建词表和对应的映射关系
word_list = [word for sentence in sentences for word in sentence]
# 去重
word_list = list(set(word_list))
# print(word_list)
word_dict = {w: i for i, w in enumerate(word_list)}
number_dict = {i: w for i, w in enumerate(word_list)}
# 词表大小
n_class = len(word_dict)
# print(n_class)

# 设置超参数
m_dim = 2  # 嵌入向量的维度
n_hidden = 10  # 神经元数量
n_step = 1  # 输入步长数
max_lens = 1  # 最大长度


# 创建输入的样本和目标值
def make_batch(sentences, max_lens, word_dict):
    input_batch = []
    target_batch = []

    for sentence in sentences:
        if len(sentence) < n_step + 1:
            continue
        for i in range(len(sentence) - n_step):
            input = [word_dict[word] for word in sentence[i:i+n_step]]
            target = word_dict[sentence[i+n_step]]

            # 填充或者裁剪确保长度等于max_lens
            input += [0] * (max_lens - len(input))
            input = input[:max_lens]

            input_batch.append(input)
            target_batch.append(target)

    return torch.LongTensor(input_batch), torch.LongTensor(target_batch)


class NNLM(nn.Module):
    def __init__(self, n_step, n_class, m_dim, n_hidden):
        super(NNLM, self).__init__()
        # 定义嵌入层,单词索引映射为嵌入向量
        self.embed = nn.Embedding(n_class, m_dim)  # 产出为每一个词的向量,为1 x m_dim的
        # 隐藏层,输入为两个词,所以采用m_dim*n_step
        self.linear1 = nn.Linear(m_dim*n_step, n_hidden)
        # 分类类别就是词表大小
        self.linear2 = nn.Linear(n_hidden, n_class)

    def forward(self, x):
        x = self.embed(x)  # 通过嵌入层得到的形状(batch_size, n_step, m_dim)->(batch_size, n_step*m_dim)
        x = x.view(-1, x.size(1) * x.size(2))
        x = self.linear1(x)
        x = torch.tanh(x)
        output = self.linear2(x)
        return output


model = NNLM(n_step, n_class, m_dim, n_hidden)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

input_batch, target_batch = make_batch(sentences, max_lens, word_dict)

for epoch in range(10000):
    optimizer.zero_grad()
    output = model(input_batch)
    loss = criterion(output, target_batch)
    loss.backward()
    optimizer.step()

    if (epoch + 1) % 1000 == 0:
        print(f"Epoch:{epoch+1}, Loss:{loss:.4f}")

# 预测
with torch.no_grad():
    predict = model(input_batch).max(1, keepdim=True)[1]
    predictions = [number_dict[n.item()] for n in predict.squeeze()]

# print([sen.split()[:2] for sentence in sentences for sen in sentence], '->', [number_dict[n.item()] for n in predict.squeeze()])
print("输入序列 -> 预测结果")
new_sentences = "床前"
count = 5
flog = True
for i in range(0, len(input_batch)):
    input_seq = []
    x = i + 1
    if x < len(input_batch):
        input_seq = [number_dict[idx.item()] for idx in input_batch[x]]
    input_seq1 = [ number_dict[ idx.item() ] for idx in input_batch[ i ] ]
    # 按词预测下一个词,并组装诗歌
    new_sentences += predictions[i]
    # 判断诗歌后是否接符号
    if len(new_sentences) % count == 0 and flog:
        new_sentences += ","
        count += 6
        flog = False
    if len(new_sentences) % count == 0 and not flog:
        new_sentences += "。"
        count += 6
        flog = True
    if len(new_sentences) % 24 == 0:
        new_sentences += input_seq[0]
    print(f"{input_seq1} -> {predictions[i]}")
print(new_sentences)

# 计算准确率
correct = (predict.squeeze() == target_batch).sum().item()
accuracy = correct / len(target_batch) * 100
print(f"Accuracy: {accuracy:.2f}%")


2.2 word2vec

Word2Vec 是一种用于生成词嵌入(word embeddings)的流行算法,由 Google 在 2013 年提出。它通过在大规模文本数据上训练神经网络模型来学习单词的向量表示。这些向量能够捕捉到单词之间的语义和句法关系,使得相似的单词在向量空间中具有相近的表示。

Word2Vec 的主要特点

  1. 低维表示
    • 将高维的稀疏向量(如 one-hot 编码)映射到低维的密集向量空间。例如,一个词汇表大小为 10,000 的 one-hot 编码可以被映射到 100 维的密集向量。
  2. 语义信息
    • 通过训练,Word2Vec 能够捕捉到输入项之间的语义关系。例如,“king” 和 “queen” 在向量空间中会非常接近,因为它们有相似的语义角色。
  3. 上下文依赖
    • Word2Vec 通常是基于上下文生成的。不同的上下文可能会导致同一个单词有不同的向量表示。例如,“bank” 在金融上下文中和在河流上下文中会有不同的向量表示。

Word2Vec 包含两种主要的模型:CBOW (Continuous Bag of Words) 和 Skip-gram。

CBOW (Continuous Bag of Words)

  • 目标:给定一个单词的上下文,预测该单词。
  • 结构:输入是上下文单词的平均向量,输出是中心单词的概率分布。
  • 优点:计算效率较高,适合处理小规模的数据集。
  • 缺点:对于罕见词的处理不如 Skip-gram 好。
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
import matplotlib.pyplot as plt


# 定义数据类型
dtype = torch.FloatTensor

# 语料库,包含训练模型的句子
sentences = ["i like dog", "i like cat", "i like animal",
             "dog cat animal", "apple cat dog like", "cat like fish",
             "dog like meat", "i like apple", "i hate apple",
             "i like movie book music apple", "dog like bark", "dog friend cat"]

# 构建词表
word_sentences = " ".join(sentences).split()
# 去重
word_list = list(set(word_sentences))
word_dict = {w: i for i, w in enumerate(word_list)}

# 创建CBOW训练数据
cbow_data = []
for i in range(1, len(word_sentences) - 1):
    context = [word_dict[word_sentences[i-1]], word_dict[word_sentences[i+1]]]
    target = word_dict[word_sentences[i]]
    cbow_data.append([context, target])

# 模型参数
voc_size = len(word_dict)
# 词向量维度
embedding_size = 2
# 批次
batch_size = 2


# 定义CBOW模型
class CBOW(nn.Module):
    def __init__(self):
        super().__init__()
        # 映射维度
        self.embed = nn.Embedding(voc_size, embedding_size)
        # 输出矩阵
        self.linear = nn.Linear(embedding_size, voc_size)

    def forward(self, x):
        embeds = self.embed(x)  # 映射矩阵, 通过嵌入层得到的形状是(batch_size, n_step, m_dim)
        avg_embeds = torch.mean(embeds, dim=1)  # 在n_step维度上平均
        output = self.linear(avg_embeds)
        return output


model = CBOW()

criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)


def random_batch(data, size):
    random_labels = []
    random_inputs = []
    # 从数据中随机选择size个索引
    random_index = np.random.choice(range(len(data)), size, replace=False)

    # 根据随机索引生成输入和标签批次
    for i in random_index:
        # 目标词one-hot编码
        random_inputs.append(data[i][0])
        # 上下文的词的索引作为标签
        random_labels.append(data[i][1])

    return random_inputs, random_labels


for epoch in range(10000):
    inputs, labels = random_batch(cbow_data, batch_size)
    inputs = np.array(inputs)
    input_batch = torch.LongTensor(inputs)
    label_batch = torch.LongTensor(labels)

    optimizer.zero_grad()
    output = model(input_batch)
    loss = criterion(output, label_batch)

    if (epoch + 1) % 1000 == 0:
        print(f"Epoch:{epoch+1}, Loss:{loss:.4f}")

    loss.backward()
    optimizer.step()


# 可视化词嵌入
for i, label in enumerate(word_list):
    # 获取模型参数
    W = model.embed.weight.data.numpy()
    x, y = float(W[i][0]), float(W[i][1])
    # 绘制散点图
    plt.scatter(x, y)
    plt.annotate(label, xy=(x, y), xytext=(5, 2), textcoords="offset points", ha="right", va="bottom")
plt.show()


Skip-gram

  • 目标:给定一个中心单词,预测其上下文中的单词。
  • 结构:输入是中心单词的向量,输出是上下文单词的概率分布。
  • 优点:能够更好地处理罕见词,生成的词向量质量较高。
  • 缺点:计算成本较高,特别是在大规模数据集上。
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
import matplotlib.pyplot as plt


# 定义数据类型
dtype = torch.FloatTensor

# 语料库,包含训练模型的句子
sentences = ["i like dog", "i like cat", "i like animal",
             "dog cat animal", "apple cat dog like", "cat like fish",
             "dog like meat", "i like apple", "i hate apple",
             "i like movie book music apple", "dog like bark", "dog friend cat"]

# 构建词表
word_sentences = " ".join(sentences).split()
word_list = list(set(word_sentences))
word_index = {w: i for i, w in enumerate(word_list)}
index_word = {i: w for i, w in enumerate(word_list)}
vocab_size = len(word_list)

# 创建skip的训练数据集
skip_grams = []
for i in range(1, len(word_sentences) - 1):
    # 当前词对应的id
    center = word_index[word_sentences[i]]
    # 获取当前词的前后两个上下文对应的词的id
    context = [word_index[word_sentences[i-1]], word_index[word_sentences[i+1]]]

    # 将当前词和上下文词组合成skip-gram数据集
    for w in context:
        skip_grams.append([center, w])

# 定义超参数
# 词汇表维度
embed_dim = 2
# 训练批次
batch_size = 5


# 定义skip模型
class Word2Vec(nn.Module):
    def __init__(self, dtype):
        super(Word2Vec, self).__init__()
        # 定义词嵌入矩阵,随机初始化,大小为(词汇表大小,词嵌入维度)
        self.W = nn.Parameter(torch.rand(vocab_size, embed_dim)).type(dtype)
        print(self.W.type())
        # 定义输出层矩阵W2, 随机初始化, 大小为(词嵌入维度, 词汇表大小)
        self.W2 = nn.Parameter(torch.rand(embed_dim, vocab_size)).type(dtype)
        self.log_softmax = nn.LogSoftmax(dim=1)

    def forward(self, x):
        # 映射层
        weights = torch.matmul(x, self.W)
        # 输出层
        out_put = torch.matmul(weights, self.W2)
        return out_put


model = Word2Vec(dtype)

criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)


def random_batch(data, size):
    random_labels = []
    random_inputs = []
    # 从数据中随机选择size个索引
    random_index = np.random.choice(range(len(data)), size, replace=False)

    # 根据随机索引生成输入和标签批次
    for i in random_index:
        # 目标词one-hot编码
        random_inputs.append(np.eye(vocab_size)[data[i][0]])
        # 上下文的词的索引作为标签
        random_labels.append(data[i][1])

    return random_inputs, random_labels


for epoch in range(10000):
    inputs, labels = random_batch(skip_grams, batch_size)
    inputs = np.array(inputs)
    input_batch = torch.from_numpy(inputs).float()
    label_batch = torch.LongTensor(labels)

    optimizer.zero_grad()
    output = model(input_batch)
    loss = criterion(output, label_batch)

    if (epoch + 1) % 1000 == 0:
        print(f"Epoch:{epoch+1}, Loss:{loss:.4f}")

    loss.backward()
    optimizer.step()

# 可视化词嵌入
for i, label in enumerate(word_list):
    # 获取模型参数
    W, W2 = model.parameters()
    x, y = float(W[i][0]), float(W[i][1])
    # 绘制散点图
    plt.scatter(x, y)
    plt.annotate(label, xy=(x, y), xytext=(5, 2), textcoords="offset points", ha="right", va="bottom")
plt.show()


gensim API调用

参数说明
sentences可以是一个list,对于大语料集,建议使用BrownCorpus,Text8Corpus或lineSentence构建。
vector_sizeword向量的维度,默认为100。大的size需要更多的训练数据,但是效果会更好。推荐值为几十到几百。
alpha学习率
window表示当前词与预测词在一个句子中的最大距离是多少。
min_count可以对字典做截断。词频少于min_count次数的单词会被丢弃掉,默认值为5。
max_vocab_size设置词向量构建期间的RAM限制。如果所有独立单词个数超过这个,则就消除掉其中最不频繁的一个。每一千万个单词需要大约1GB的RAM。设置成None则没有限制。
sample高频词汇的随机降采样的配置阈值,默认为1e-3,范围是(0,1e-5)
seed用于随机数发生器。与初始化词向量有关。
workers参数控制训练的并行数。
sg用于设置训练算法,默认为0,对应CBOW算法;sg=1则采用skip-gram算法。
hs如果为1则会采用hierarchica·softmax技巧。如果设置为0(default),则negative sampling会被使用。
negative如果>0,则会采用negative samping,用于设置多少个noise words。
cbow_mean如果为0,则采用上下文词向量的和,如果为1(default)则采用均值。只有使用CBOW的时候才起作用。
hashfxnhash函数来初始化权重。默认使用python的hash函数。
epochs迭代次数,默认为5。
trim_rule用于设置词汇表的整理规则,指定那些单词要留下,哪些要被删除。可以设置为None(min_count会被使用)或者一个接受()并返回RULE_DISCARD,utils。RULE_KEEP或者utils。RULE_DEFAULT的函数。
sorted_vocab如果为1(default),则在分配word index 的时候会先对单词基于频率降序排序。
batch_words每一批的传递给线程的单词的数量,默认为10000
min_alpha随着训练的进行,学习率线性下降到min_alpha
import numpy as np
from gensim.models import Word2Vec
import matplotlib.pyplot as plt


# 语料库,包含训练模型的句子
sentences = ["i like dog", "i like cat", "i like animal",
             "dog cat animal", "apple cat dog like", "cat like fish",
             "dog like meat", "i like apple", "i hate apple",
             "i like movie book music apple", "dog like bark", "dog friend cat"]

# 每个句子分成单词表(word2vec api要求输入格式为二维列表: 每一个句子是一个单词列表
token_list = [sentence.split() for sentence in sentences]

# 定义word2vec模型
model = Word2Vec(token_list, vector_size=2, window=1, min_count=0, sg=1)

# 获取词汇表
word_list = list(model.wv.index_to_key)

# 可视化词嵌入
for i, word in enumerate(word_list):
    # 获取模型参数
    W = model.wv[word]
    x, y = float(W[0]), float(W[1])
    # 绘制散点图
    plt.scatter(x, y)
    plt.annotate(word, xy=(x, y), xytext=(5, 2), textcoords="offset points", ha="right", va="bottom")
plt.show()

print(model.wv.most_similar("dog"))
# 打印两个词的相似度
print(model.wv.similarity("dog", "cat"))



http://www.kler.cn/a/321106.html

相关文章:

  • 测试实项中的偶必现难测bug--互斥逻辑异常
  • RabbitMQ实战启程:从原理到部署的全方位探索(上)
  • 【大数据测试 Hive数据库--保姆级教程】
  • ffmpeg视频编码
  • 4. Spring Cloud Ribbon 实现“负载均衡”的详细配置说明
  • debian 系统更新升级
  • PostgreSQL 的log_hostname 参数测试
  • 搭建cdh集群及问题处理
  • HandlerInterceptor这个类有什么作用?
  • 基于JAVA+SpringBoot+Vue的健身房管理系统1
  • Redis Sorted Set 跳表的实现原理和分析
  • 数据结构升华部分:排序与字符串匹配算法应用
  • 产品经理面试整理-练习常见面试问题
  • 【Linux】Linux 的 权限
  • 钉钉 钉钉打卡 钉钉定位 2024 免费试用 保用
  • 【运维】微软官方包管理器winget的使用, 对比scoop/choco(含常用软件清单,本地镜像源自建,静默安装教程)
  • Spring Boot 中整合 Kafka
  • EAGLE——探索混合编码器的多模态大型语言模型的设计空间
  • BOE(京东方)重磅亮相世界制造业大会 科技创新引领现代化产业体系建设新未来
  • Tengine 容器
  • HTML开发指南
  • web基础:域名、网页、HTML、web版本
  • Excel--DATEDIF函数的用法及参数含义
  • 根据软件架构设计与评估的叙述开发一套机器学习应用开发平台
  • jsonschema - 校验Json内容和格式
  • Python知识点:如何使用Python进行区块链开发