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

深度学习笔记之自然语言处理(NLP)

深度学习笔记之自然语言处理(NLP)

在行将开学之时,我将开始我的深度学习笔记的自然语言处理部分,这部分内容是在前面基础上开展学习的,且目前我的学习更加倾向于通识。自然语言处理部分将包含《动手学深度学习》这本书的第十四章,自然语言处理预训练和第十五章,自然语言处理应用。并且参考原书提供的jupyter notebook资源。

自然语言处理,预训练

自然语言处理(Natural Language Processing,简称NLP)是人工智能的一个重要分支,它涉及计算机和人类(自然)语言之间的互动。自然语言处理的主要目标是让计算机能够理解和解释人类语言的方式,以便于能够执行如自动翻译、情感分析、信息提取、文本摘要、语音识别等任务。自然语言处理简而言之就是让计算机可以“理解”人类的语言,现在大语言模型就做到了这一点。

词嵌入

词嵌入的英文名为word2vec(这里的2意思就是to,在很多函数命名都是如此来用)。词嵌入是用来捕捉单词中的语义信息的。词嵌入(Word Embedding)是自然语言处理(NLP)中的一种重要技术,它将词汇映射到高维空间的向量中,使得语义上相似的词在向量空间中的距离也相近。词嵌入能够有效地捕捉单词的语义信息,是深度学习在自然语言处理中的一个基础组成部分。

以下是一些关于词嵌入的详细介绍:

  • 向量化表示:在词嵌入之前,文本通常以独热编码(One-Hot Encoding)的形式表示,这种表示方式维度高且稀疏,无法体现词与词之间的关系。而词嵌入则提供了低维、稠密的向量表示。

  • 语义信息:通过词嵌入,语义相似的词在向量空间中的位置相近,这使得模型能够更好地理解和处理自然语言。

总的来说词向量的特征如下:

  • 词向量是用于表示单词意义的向量,也可以看作词的特征向量。将词映射到实向量的技术称为词嵌入。

  • word2vec工具包含跳元模型和连续词袋模型。

  • 跳元模型假设一个单词可用于在文本序列中,生成其周围的单词;而连续词袋模型假设基于上下文词来生成中心单词。

近似训练

本部分中介绍了负采样和分层softmax这两种用于处理跳元模型(连续词袋模型)的近似训练方法。

负采样(Negative Sampling)是深度学习中特别是在自然语言处理(NLP)领域常用的一种技术,尤其是在训练词嵌入模型(如Word2Vec)和推荐系统等场景中。负采样的主要目的是提高训练效率,减少计算量。在深度学习中,很多任务可以被视为分类问题,其中正样本是任务相关的正确示例,而负样本则是不相关的示例。例如,在词嵌入模型中,一个词的上下文(周围的词)可以看作是正样本,而其他不相关的词则可以作为负样本。

层序softmax(Hierarchical Softmax)是一种用于加速深度学习模型训练过程中softmax运算的技术,尤其是在处理大规模分类问题,如语言模型中的词汇表非常大时。层序softmax通过将分类问题转化为一系列二分类问题来减少计算量。

在标准的softmax运算中,我们需要计算每个类别对应的概率,这涉及到对所有类别的指数函数的计算和归一化,其计算复杂度与类别数量成线性关系。当类别数量非常大时(例如,词汇表中有数万或数十万个单词),这种计算变得非常耗时。

层序softmax通过构建一个二叉树(通常是哈夫曼树)来表示所有的类别,每个叶节点代表一个类别。在训练过程中,我们不再直接计算所有类别的概率,而是通过在二叉树上进行一系列的二分类决策来计算目标类别的概率。

来自Transformer的双向编码器表示(BERT)

BERT(Bidirectional Encoder Representations from Transformers)是一种预训练自然语言处理模型,由Google在2018年提出。BERT在自然语言处理(NLP)领域具有重大意义,因为它为多种NLP任务提供了强大的基础模型。

以下是BERT的一些主要特点:

  1. 双向性(Bidirectional):与传统语言模型不同,BERT是一个双向模型,这意味着它同时考虑输入文本的左右上下文。这种双向性使得BERT能够更准确地捕捉词汇的语义信息。

  2. Transformer架构:BERT基于Transformer模型,这是一种基于自注意力机制的深度神经网络架构。Transformer能够处理长距离的依赖关系,这在NLP任务中非常重要。

  3. 预训练任务

    • 掩码语言模型(Masked Language Model, MLM):在输入文本中随机掩盖一些词汇,然后让模型预测这些被掩盖的词汇。这种任务迫使模型同时考虑被掩盖词汇的左右上下文。

    • 下一句预测(Next Sentence Prediction, NSP):给定两个句子A和B,模型需要预测B是否是A的下一句。这个任务有助于模型理解句子之间的关系。

  4. 微调(Fine-tuning):BERT通过预训练任务学习通用的语言表示,然后在特定任务上进行微调。微调阶段,BERT可以根据具体任务调整模型参数,从而在各个NLP任务上取得很好的表现。

BERT在以下NLP任务中表现出色:

  • 文本分类

  • 命名实体识别

  • 问答系统

  • 自然语言推理

  • 机器翻译

  • 文本生成等

BERT的提出极大地推动了NLP领域的发展,为许多NLP任务提供了有效的解决方案。如今,BERT及其变体(如RoBERTa、ALBERT、ERNIE等)在学术界和工业界都得到了广泛应用。部分代码如下所示:

import torch
from torch import nn
from d2l import torch as d2l
#@save
def get_tokens_and_segments(tokens_a, tokens_b=None):"""获取输入序列的词元及其片段索引"""tokens = ['<cls>'] + tokens_a + ['<sep>']# 0和1分别标记片段A和Bsegments = [0] * (len(tokens_a) + 2)if tokens_b is not None:tokens += tokens_b + ['<sep>']segments += [1] * (len(tokens_b) + 1)return tokens, segments
#@save
class BERTEncoder(nn.Module):"""BERT编码器"""def __init__(self, vocab_size, num_hiddens, norm_shape, ffn_num_input,ffn_num_hiddens, num_heads, num_layers, dropout,max_len=1000, key_size=768, query_size=768, value_size=768,**kwargs):super(BERTEncoder, self).__init__(**kwargs)self.token_embedding = nn.Embedding(vocab_size, num_hiddens)self.segment_embedding = nn.Embedding(2, num_hiddens)self.blks = nn.Sequential()for i in range(num_layers):self.blks.add_module(f"{i}", d2l.EncoderBlock(key_size, query_size, value_size, num_hiddens, norm_shape,ffn_num_input, ffn_num_hiddens, num_heads, dropout, True))# 在BERT中,位置嵌入是可学习的,因此我们创建一个足够长的位置嵌入参数self.pos_embedding = nn.Parameter(torch.randn(1, max_len,num_hiddens))
​def forward(self, tokens, segments, valid_lens):# 在以下代码段中,X的形状保持不变:(批量大小,最大序列长度,num_hiddens)X = self.token_embedding(tokens) + self.segment_embedding(segments)X = X + self.pos_embedding.data[:, :X.shape[1], :]for blk in self.blks:X = blk(X, valid_lens)return X
vocab_size, num_hiddens, ffn_num_hiddens, num_heads = 10000, 768, 1024, 4
norm_shape, ffn_num_input, num_layers, dropout = [768], 768, 2, 0.2
encoder = BERTEncoder(vocab_size, num_hiddens, norm_shape, ffn_num_input,ffn_num_hiddens, num_heads, num_layers, dropout)
tokens = torch.randint(0, vocab_size, (2, 8))
segments = torch.tensor([[0, 0, 0, 0, 1, 1, 1, 1], [0, 0, 0, 1, 1, 1, 1, 1]])
encoded_X = encoder(tokens, segments, None)
encoded_X.shape
#@save
class MaskLM(nn.Module):"""BERT的掩蔽语言模型任务"""def __init__(self, vocab_size, num_hiddens, num_inputs=768, **kwargs):super(MaskLM, self).__init__(**kwargs)self.mlp = nn.Sequential(nn.Linear(num_inputs, num_hiddens),nn.ReLU(),nn.LayerNorm(num_hiddens),nn.Linear(num_hiddens, vocab_size))
​def forward(self, X, pred_positions):num_pred_positions = pred_positions.shape[1]pred_positions = pred_positions.reshape(-1)batch_size = X.shape[0]batch_idx = torch.arange(0, batch_size)# 假设batch_size=2,num_pred_positions=3# 那么batch_idx是np.array([0,0,0,1,1,1])batch_idx = torch.repeat_interleave(batch_idx, num_pred_positions)masked_X = X[batch_idx, pred_positions]masked_X = masked_X.reshape((batch_size, num_pred_positions, -1))mlm_Y_hat = self.mlp(masked_X)return mlm_Y_hat
mlm = MaskLM(vocab_size, num_hiddens)
mlm_positions = torch.tensor([[1, 5, 2], [6, 1, 5]])
mlm_Y_hat = mlm(encoded_X, mlm_positions)
mlm_Y_hat.shape
mlm_Y = torch.tensor([[7, 8, 9], [10, 20, 30]])
loss = nn.CrossEntropyLoss(reduction='none')
mlm_l = loss(mlm_Y_hat.reshape((-1, vocab_size)), mlm_Y.reshape(-1))
mlm_l.shape
#@save
class NextSentencePred(nn.Module):"""BERT的下一句预测任务"""def __init__(self, num_inputs, **kwargs):super(NextSentencePred, self).__init__(**kwargs)self.output = nn.Linear(num_inputs, 2)
​def forward(self, X):# X的形状:(batchsize,num_hiddens)return self.output(X)
encoded_X = torch.flatten(encoded_X, start_dim=1)
# NSP的输入形状:(batchsize,num_hiddens)
nsp = NextSentencePred(encoded_X.shape[-1])
nsp_Y_hat = nsp(encoded_X)
nsp_Y_hat.shape
nsp_y = torch.tensor([0, 1])
nsp_l = loss(nsp_Y_hat, nsp_y)
nsp_l.shape
#@save
class BERTModel(nn.Module):"""BERT模型"""def __init__(self, vocab_size, num_hiddens, norm_shape, ffn_num_input,ffn_num_hiddens, num_heads, num_layers, dropout,max_len=1000, key_size=768, query_size=768, value_size=768,hid_in_features=768, mlm_in_features=768,nsp_in_features=768):super(BERTModel, self).__init__()self.encoder = BERTEncoder(vocab_size, num_hiddens, norm_shape,ffn_num_input, ffn_num_hiddens, num_heads, num_layers,dropout, max_len=max_len, key_size=key_size,query_size=query_size, value_size=value_size)self.hidden = nn.Sequential(nn.Linear(hid_in_features, num_hiddens),nn.Tanh())self.mlm = MaskLM(vocab_size, num_hiddens, mlm_in_features)self.nsp = NextSentencePred(nsp_in_features)
​def forward(self, tokens, segments, valid_lens=None,pred_positions=None):encoded_X = self.encoder(tokens, segments, valid_lens)if pred_positions is not None:mlm_Y_hat = self.mlm(encoded_X, pred_positions)else:mlm_Y_hat = None# 用于下一句预测的多层感知机分类器的隐藏层,0是“<cls>”标记的索引nsp_Y_hat = self.nsp(self.hidden(encoded_X[:, 0, :]))return encoded_X, mlm_Y_hat, nsp_Y_hat

自然语言处理,应用

本章将探讨两种流行的自然语言处理任务,分别是:情感分析和自然语言推断,并在预训练和相应架构的基础上开展应用。

情感分析

情感分析与数据集

情感分析的数据集是来自于斯坦福大学的,数据集中包含上万条评论,且包含两种标签,分别是积极和消极。对数据集的读取和分析代码如下所示.

import os
import torch
from torch import nn
import d2l
#@save
d2l.DATA_HUB['aclImdb'] = ('http://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz','01ada507287d82875905620988597833ad4e0903')
​
data_dir = d2l.download_extract('aclImdb', 'aclImdb')
#@save
def read_imdb(data_dir, is_train):"""读取IMDb评论数据集文本序列和标签"""data, labels = [], []for label in ('pos', 'neg'):#两种标签分别为postive 和negativefolder_name = os.path.join(data_dir, 'train' if is_train else 'test',label)for file in os.listdir(folder_name):with open(os.path.join(folder_name, file), 'rb') as f:review = f.read().decode('utf-8').replace('\n', '')data.append(review)labels.append(1 if label == 'pos' else 0)return data, labels
​
train_data = read_imdb(data_dir, is_train=True)
print('训练集数目:', len(train_data[0]))
for x, y in zip(train_data[0][:3], train_data[1][:3]):print('标签:', y, 'review:', x[0:60])
train_tokens = d2l.tokenize(train_data[0], token='word')
vocab = d2l.Vocab(train_tokens, min_freq=5, reserved_tokens=['<pad>'])#进行词频过滤
d2l.set_figsize()
d2l.plt.xlabel('# tokens per review')
d2l.plt.ylabel('count')
d2l.plt.hist([len(line) for line in train_tokens], bins=range(0, 1000, 50))
num_steps = 500  # 序列长度
train_features = torch.tensor([d2l.truncate_pad(vocab[line], num_steps, vocab['<pad>']) for line in train_tokens])
print(train_features.shape)
train_iter = d2l.load_array((train_features,torch.tensor(train_data[1])), 64)
​
for X, y in train_iter:print('X:', X.shape, ', y:', y.shape)break
print('小批量数目:', len(train_iter))
#@save
def load_data_imdb(batch_size, num_steps=500):"""返回数据迭代器和IMDb评论数据集的词表"""data_dir = d2l.download_extract('aclImdb', 'aclImdb')train_data = read_imdb(data_dir, True)test_data = read_imdb(data_dir, False)train_tokens = d2l.tokenize(train_data[0], token='word')test_tokens = d2l.tokenize(test_data[0], token='word')vocab = d2l.Vocab(train_tokens, min_freq=5)train_features = torch.tensor([d2l.truncate_pad(vocab[line], num_steps, vocab['<pad>']) for line in train_tokens])test_features = torch.tensor([d2l.truncate_pad(vocab[line], num_steps, vocab['<pad>']) for line in test_tokens])train_iter = d2l.load_array((train_features, torch.tensor(train_data[1])),batch_size)test_iter = d2l.load_array((test_features, torch.tensor(test_data[1])),batch_size,is_train=False)return train_iter, test_iter, vocab

输出的词元长度的直方图如下:

          

情感分析与循环神经网络

循环神经网络(RNN)非常适合用于处理文本数据,因为它能够考虑到序列数据中的时间动态特性,这在文本中体现为词语的顺序。以下是使用循环神经网络来研究文本中情感的基本步骤:

  1. 数据收集与预处理

  • 收集数据集:获取一个标注好的情感数据集,如IMDb电影评论数据集,其中包含了正面和负面情感的评论。

  • 文本清洗:去除无关字符,如HTML标签、特殊符号等。

  • 分词:将文本分割成单词或词语。

  • 构建词汇表:将文本中的单词映射为唯一的整数索引。

  • 填充与截断:确保所有文本序列长度一致,通过填充或截断来实现。

  1. 构建模型

  • 选择RNN类型:可以使用标准的RNN,或者更高级的变体如LSTM(长短期记忆网络)或GRU(门控循环单元),这些变体能够更好地捕捉长距离依赖。

  • 定义网络结构

  1. 训练模型

  • 损失函数:对于分类问题,通常使用交叉熵损失函数。

  • 优化器:选择一个优化器,如Adam,来调整网络权重。

  • 迭代训练:通过多次迭代训练数据来优化网络权重。

  1. 评估模型

  • 划分数据集:将数据集划分为训练集、验证集和测试集。

  • 评估指标:使用准确率、召回率、F1分数等指标来评估模型性能。

  1. 模型调优

  • 调整超参数:根据验证集的性能来调整学习率、批次大小、网络层数等超参数。

  • 防止过拟合:使用正则化技术如Dropout,或者提前停止训练来防止过拟合。

  1. 应用模型

  • 情感分析:使用训练好的模型对新的文本数据进行情感分类。

完成的代码如下所示:

import torch
from torch import nn
import d2l
batch_size = 64
train_iter, test_iter, vocab = d2l.load_data_imdb(batch_size)
class BiRNN(nn.Module):def __init__(self, vocab_size, embed_size, num_hiddens,num_layers, **kwargs):super(BiRNN, self).__init__(**kwargs)self.embedding = nn.Embedding(vocab_size, embed_size)# 将bidirectional设置为True以获取双向循环神经网络self.encoder = nn.LSTM(embed_size, num_hiddens, num_layers=num_layers,bidirectional=True)self.decoder = nn.Linear(4 * num_hiddens, 2)
​def forward(self, inputs):# inputs的形状是(批量大小,时间步数)# 因为长短期记忆网络要求其输入的第一个维度是时间维,# 所以在获得词元表示之前,输入会被转置。# 输出形状为(时间步数,批量大小,词向量维度)embeddings = self.embedding(inputs.T)self.encoder.flatten_parameters()# 返回上一个隐藏层在不同时间步的隐状态,# outputs的形状是(时间步数,批量大小,2*隐藏单元数)outputs, _ = self.encoder(embeddings)# 连结初始和最终时间步的隐状态,作为全连接层的输入,# 其形状为(批量大小,4*隐藏单元数)encoding = torch.cat((outputs[0], outputs[-1]), dim=1)outs = self.decoder(encoding)return outs
embed_size, num_hiddens, num_layers = 100, 100, 2
devices = d2l.try_all_gpus()
net = BiRNN(len(vocab), embed_size, num_hiddens, num_layers)#构造具有两个隐藏层的双向循环神经网络
def init_weights(m):#初始化模型参数if type(m) == nn.Linear:nn.init.xavier_uniform_(m.weight)if type(m) == nn.LSTM:for param in m._flat_weights_names:if "weight" in param:nn.init.xavier_uniform_(m._parameters[param])
net.apply(init_weights)
glove_embedding = d2l.TokenEmbedding('glove.6b.100d')#加载预训练的Glove嵌入
embeds = glove_embedding[vocab.idx_to_token]
embeds.shape
net.embedding.weight.data.copy_(embeds)
net.embedding.weight.requires_grad = False
lr, num_epochs = 0.01, 5
trainer = torch.optim.Adam(net.parameters(), lr=lr)
loss = nn.CrossEntropyLoss(reduction="none")
d2l.train_ch13(net, train_iter, test_iter, loss, trainer, num_epochs,#训练模型devices)
#@save
def predict_sentiment(net, vocab, sequence):"""预测文本序列的情感"""sequence = torch.tensor(vocab[sequence.split()], device=d2l.try_gpu())label = torch.argmax(net(sequence.reshape(1, -1)), dim=1)return 'positive' if label == 1 else 'negative'
predict_sentiment(net, vocab, 'this movie is so great')#结果是Positive
predict_sentiment(net, vocab, 'this movie is so bad')#结果是negative

          

训练结果是判断相当的准确,但凡给出稍微正常的评价语句就能分成积极的和消极的。

在深度学习中,预训练(Pre-training)是一种训练策略,指的是在一个大型数据集上对模型进行初步训练,然后再在特定任务的数据集上进行微调(Fine-tuning)。以下是预训练的几个关键概念:

  1. 预训练的目的

  • 学习通用特征:预训练通常在大规模数据集上进行,目的是让模型学习到通用的特征表示,这些特征可以在多种不同的任务中重用。

  • 减少对标注数据的依赖:标注数据通常非常昂贵且耗时,预训练可以让模型在没有大量标注数据的情况下也能获得较好的性能。

  • 提高模型性能:预训练可以作为一种正则化手段,帮助模型在小数据集上避免过拟合,并提高泛化能力。

  1. 预训练的方法

  • 自监督学习:在没有标注数据的情况下,通过设计预测任务来训练模型,例如预测句子中的下一个词(语言模型预训练)或预测图像中的像素块(如MAE)。

  • 监督学习:使用有标注的大数据集进行训练,如ImageNet数据集上的图像分类任务。

  • 迁移学习:在源任务上预训练模型,然后将模型迁移到目标任务上进行微调。

  1. 预训练模型

  • BERT(Bidirectional Encoder Representations from Transformers):一种基于Transformer的双向编码器,通过预测句子中被掩盖的词来进行预训练。

  • GPT(Generative Pre-trained Transformer):一种基于Transformer的生成模型,通过语言建模任务进行预训练。

  • ResNet:一种深度卷积神经网络,通常在ImageNet数据集上进行预训练,用于图像识别任务。

情感分析与卷积神经网络

在这个例子中,我们将使用一个简单的卷积神经网络(CNN)来进行文本情感分析。以下是使用CNN进行文本情感分析的基本步骤:

  1. 数据预处理

    • 分词:将文本数据分割成单词或字符。

    • 嵌入:使用预训练的词向量(如Word2Vec、GloVe)将单词转换为固定长度的向量。

    • 填充:确保所有文本序列长度一致,通常通过在序列末尾添加特殊填充值(如0)。

  2. 构建卷积神经网络

    • 嵌入层:将单词索引映射到密集的向量表示。

    • 卷积层:使用多个卷积核提取特征。

    • 激活函数:通常使用ReLU函数。

    • 池化层:减少特征维度,同时保留重要信息。

    • 全连接层:将提取的特征映射到情感类别。

  3. 训练模型

    • 损失函数:通常使用交叉熵损失函数。

    • 优化器:如Adam、SGD等。

  4. 评估模型

    • 使用验证集评估模型性能。

    • 可以使用准确率、F1分数等指标来衡量模型效果。

示例代码如下:

import torch
from torch import nn
import d2l
batch_size = 64#定义批量大小
train_iter, test_iter, vocab = d2l.load_data_imdb(batch_size)#加载数据
def corr1d(X, K):#定义一个一维互相关函数w = K.shape[0]Y = torch.zeros((X.shape[0] - w + 1))for i in range(Y.shape[0]):Y[i] = (X[i: i + w] * K).sum()return Y
X, K = torch.tensor([0, 1, 2, 3, 4, 5, 6]), torch.tensor([1, 2])
corr1d(X, K)#测试一维互相关函数
def corr1d_multi_in(X, K):# 首先,遍历'X'和'K'的第0维(通道维)。然后,把它们加在一起return sum(corr1d(x, k) for x, k in zip(X, K))
​
X = torch.tensor([[0, 1, 2, 3, 4, 5, 6],[1, 2, 3, 4, 5, 6, 7],[2, 3, 4, 5, 6, 7, 8]])
K = torch.tensor([[1, 2], [3, 4], [-1, -3]])
corr1d_multi_in(X, K)
class TextCNN(nn.Module):#定义一个卷积神经网络类,继承自nn.Moduledef __init__(self, vocab_size, embed_size, kernel_sizes, num_channels,**kwargs):super(TextCNN, self).__init__(**kwargs)for c, k in zip(num_channels, kernel_sizes):self.convs.append(nn.Conv1d(2 * embed_size, c, k))
​def forward(self, inputs):# 沿着向量维度将两个嵌入层连结起来,# 每个嵌入层的输出形状都是(批量大小,词元数量,词元向量维度)连结起来embeddings = torch.cat((self.embedding(inputs), self.constant_embedding(inputs)), dim=2)# 根据一维卷积层的输入格式,重新排列张量,以便通道作为第2维embeddings = embeddings.permute(0, 2, 1)# 每个一维卷积层在最大时间汇聚层合并后,获得的张量形状是(批量大小,通道数,1)# 删除最后一个维度并沿通道维度连结encoding = torch.cat([torch.squeeze(self.relu(self.pool(conv(embeddings))), dim=-1)for conv in self.convs], dim=1)outputs = self.decoder(self.dropout(encoding))return outputs
embed_size, kernel_sizes, nums_channels = 100, [3, 4, 5], [100, 100, 100]
devices = d2l.try_all_gpus()#采用GPU来训练
net = TextCNN(len(vocab), embed_size, kernel_sizes, nums_channels)#新建一个神经网络
​
def init_weights(m):if type(m) in (nn.Linear, nn.Conv1d):nn.init.xavier_uniform_(m.weight)
​
net.apply(init_weights)#调用属性函数
glove_embedding = d2l.TokenEmbedding('glove.6b.100d')#加载预训练的Glove嵌入
embeds = glove_embedding[vocab.idx_to_token]
net.embedding.weight.data.copy_(embeds)
net.constant_embedding.weight.data.copy_(embeds)
net.constant_embedding.weight.requires_grad = False
lr, num_epochs = 0.001, 5
trainer = torch.optim.Adam(net.parameters(), lr=lr)
loss = nn.CrossEntropyLoss(reduction="none")
d2l.train_ch13(net, train_iter, test_iter, loss, trainer, num_epochs, devices)#采用d2l包中的训练函数
d2l.predict_sentiment(net, vocab, 'this movie is so great')#结果为Positive
d2l.predict_sentiment(net, vocab, 'this movie is so bad')#结果为negative

在Textcnn模型中使用一维卷积层和最大时间汇聚层将单个词元表示转化为下游的应用输出。

自然语言推断

自然语言推断与数据集

自然语言推断(Natural Language Inference, NLI)是自然语言处理(Natural Language Processing, NLP)领域的一个重要研究方向,它研究的是如何让计算机理解和推断两个自然语言表达(通常是一个前提(premise)和一个假设(hypothesis))之间的逻辑关系。自然语言推断的主要研究内容包括以下几个方面:

  1. 关系分类:自然语言推断的核心任务是判断两个句子之间的逻辑关系,这通常分为三个类别:

    • 蕴含(Entailment):假设是前提的必然结果。

    • 矛盾(Contradiction):假设与前提完全相反,不能同时为真。

    • 中性(Neutral):假设既不是前提的必然结果,也不与前提矛盾。

  2. 模型架构:研究者们探索了多种深度学习模型架构来提高自然语言推断的性能,包括:

    • 卷积神经网络(CNN):用于捕捉局部特征和句子的层次结构。

    • 循环神经网络(RNN):特别是长短期记忆网络(LSTM)和门控循环单元(GRU),用于处理序列数据。

    • 变压器(Transformer)模型:利用自注意力机制,能够处理长距离依赖,是目前NLI研究中的主流模型。

    • 预训练模型:如BERT(Bidirectional Encoder Representations from Transformers)、RoBERTa、ALBERT等,这些模型在大型语料库上预训练,然后在NLI任务上进行微调。

 

import os
import re
import torch
from torch import nn
import d2l
#@save
d2l.DATA_HUB['SNLI'] = ('https://nlp.stanford.edu/projects/snli/snli_1.0.zip','9fcde07509c7e87ec61c640c1b2753d9041758e4')
​
data_dir = d2l.download_extract('SNLI')#下载斯坦福自然语言数据集
#@save
def read_snli(data_dir, is_train):"""将SNLI数据集解析为前提、假设和标签"""def extract_text(s):# 删除我们不会使用的信息s = re.sub('\\(', '', s)s = re.sub('\\)', '', s)# 用一个空格替换两个或多个连续的空格s = re.sub('\\s{2,}', ' ', s)return s.strip()label_set = {'entailment': 0, 'contradiction': 1, 'neutral': 2}file_name = os.path.join(data_dir, 'snli_1.0_train.txt'if is_train else 'snli_1.0_test.txt')with open(file_name, 'r') as f:rows = [row.split('\t') for row in f.readlines()[1:]]premises = [extract_text(row[1]) for row in rows if row[0] in label_set]hypotheses = [extract_text(row[2]) for row in rows if row[0] \in label_set]labels = [label_set[row[0]] for row in rows if row[0] in label_set]return premises, hypotheses, labels
train_data = read_snli(data_dir, is_train=True)
for x0, x1, y in zip(train_data[0][:3], train_data[1][:3], train_data[2][:3]):print('前提:', x0)print('假设:', x1)print('标签:', y)
test_data = read_snli(data_dir, is_train=False)
for data in [train_data, test_data]:print([[row for row in data[2]].count(i) for i in range(3)])
#@save
class SNLIDataset(torch.utils.data.Dataset):"""用于加载SNLI数据集的自定义数据集"""def __init__(self, dataset, num_steps, vocab=None):self.num_steps = num_stepsall_premise_tokens = d2l.tokenize(dataset[0])all_hypothesis_tokens = d2l.tokenize(dataset[1])if vocab is None:self.vocab = d2l.Vocab(all_premise_tokens + \all_hypothesis_tokens, min_freq=5, reserved_tokens=['<pad>'])else:self.vocab = vocabself.premises = self._pad(all_premise_tokens)self.hypotheses = self._pad(all_hypothesis_tokens)self.labels = torch.tensor(dataset[2])print('read ' + str(len(self.premises)) + ' examples')
​def _pad(self, lines):#填充return torch.tensor([d2l.truncate_pad(self.vocab[line], self.num_steps, self.vocab['<pad>'])for line in lines])
​def __getitem__(self, idx):#返回数据集中索引为idx的元素return (self.premises[idx], self.hypotheses[idx]), self.labels[idx]
​def __len__(self):#返回数据集中的元素数量return len(self.premises)
#@save
def load_data_snli(batch_size, num_steps=50):"""下载SNLI数据集并返回数据迭代器和词表"""num_workers = d2l.get_dataloader_workers()data_dir = d2l.download_extract('SNLI')train_data = read_snli(data_dir, True)test_data = read_snli(data_dir, False)train_set = SNLIDataset(train_data, num_steps)test_set = SNLIDataset(test_data, num_steps, train_set.vocab)train_iter = torch.utils.data.DataLoader(train_set, batch_size,shuffle=True,num_workers=num_workers)test_iter = torch.utils.data.DataLoader(test_set, batch_size,shuffle=False,num_workers=num_workers)return train_iter, test_iter, train_set.vocab
train_iter, test_iter, vocab = load_data_snli(128, 50)#批量大小为128,每个序列长度为50
len(vocab)#词表大小
for X, Y in train_iter:#X是一个元组,包含两个形状为(批量大小, 词数)的张量print(X[0].shape)print(X[1].shape)print(Y.shape)break

自然语言推断:使用注意力

注意力机制在自然语言推断中的应用

在自然语言推断中,注意力机制通常用于以下步骤:

  1. 词嵌入层

首先,将输入的前提(premise)和假设(hypothesis)中的每个单词转换为其对应的词向量表示。

  1. 编码层

使用循环神经网络(RNN)或变压器(Transformer)的编码器层来处理这些词向量,以获得每个单词的上下文表示。

  1. 注意力层

在编码层之后,引入注意力层来计算前提和假设之间的交互。以下是几种常见的注意力机制:

使用注意力机制的自然语言推断的代码如下:

 

import torch
from torch import nn
from torch.nn import functional as F
import d2l
def mlp(num_inputs, num_hiddens, flatten):#多层感知机net = []net.append(nn.Dropout(0.2))net.append(nn.Linear(num_inputs, num_hiddens))net.append(nn.ReLU())if flatten:net.append(nn.Flatten(start_dim=1))net.append(nn.Dropout(0.2))net.append(nn.Linear(num_hiddens, num_hiddens))net.append(nn.ReLU())if flatten:net.append(nn.Flatten(start_dim=1))return nn.Sequential(*net)
class Attend(nn.Module):#注意力机制def __init__(self, num_inputs, num_hiddens, **kwargs):super(Attend, self).__init__(**kwargs)self.f = mlp(num_inputs, num_hiddens, flatten=False)
​def forward(self, A, B):# A/B的形状:(批量大小,序列A/B的词元数,embed_size)# f_A/f_B的形状:(批量大小,序列A/B的词元数,num_hiddens)f_A = self.f(A)f_B = self.f(B)# e的形状:(批量大小,序列A的词元数,序列B的词元数)e = torch.bmm(f_A, f_B.permute(0, 2, 1))# beta的形状:(批量大小,序列A的词元数,embed_size),# 意味着序列B被软对齐到序列A的每个词元(beta的第1个维度)beta = torch.bmm(F.softmax(e, dim=-1), B)# beta的形状:(批量大小,序列B的词元数,embed_size),# 意味着序列A被软对齐到序列B的每个词元(alpha的第1个维度)alpha = torch.bmm(F.softmax(e.permute(0, 2, 1), dim=-1), A)return beta, alpha
class Compare(nn.Module):#比较def __init__(self, num_inputs, num_hiddens, **kwargs):super(Compare, self).__init__(**kwargs)self.g = mlp(num_inputs, num_hiddens, flatten=False)
​def forward(self, A, B, beta, alpha):V_A = self.g(torch.cat([A, beta], dim=2))V_B = self.g(torch.cat([B, alpha], dim=2))return V_A, V_B
class Aggregate(nn.Module):#聚合def __init__(self, num_inputs, num_hiddens, num_outputs, **kwargs):super(Aggregate, self).__init__(**kwargs)self.h = mlp(num_inputs, num_hiddens, flatten=True)self.linear = nn.Linear(num_hiddens, num_outputs)
​def forward(self, V_A, V_B):# 对两组比较向量分别求和V_A = V_A.sum(dim=1)V_B = V_B.sum(dim=1)# 将两个求和结果的连结送到多层感知机中Y_hat = self.linear(self.h(torch.cat([V_A, V_B], dim=1)))return Y_hat
class DecomposableAttention(nn.Module):#分解注意力def __init__(self, vocab, embed_size, num_hiddens, num_inputs_attend=100,num_inputs_compare=200, num_inputs_agg=400, **kwargs):super(DecomposableAttention, self).__init__(**kwargs)self.embedding = nn.Embedding(len(vocab), embed_size)self.attend = Attend(num_inputs_attend, num_hiddens)self.compare = Compare(num_inputs_compare, num_hiddens)# 有3种可能的输出:蕴涵、矛盾和中性self.aggregate = Aggregate(num_inputs_agg, num_hiddens, num_outputs=3)
​def forward(self, X):#前向传播函数premises, hypotheses = XA = self.embedding(premises)B = self.embedding(hypotheses)beta, alpha = self.attend(A, B)V_A, V_B = self.compare(A, B, beta, alpha)Y_hat = self.aggregate(V_A, V_B)return Y_hat
batch_size, num_steps = 256, 50#批量大小为256,每个序列长度为50
train_iter, test_iter, vocab = d2l.load_data_snli(batch_size, num_steps)#加载SNLI数据集
embed_size, num_hiddens, devices = 100, 200, d2l.try_all_gpus()#嵌入维度为100,隐藏单元数为200
net = DecomposableAttention(vocab, embed_size, num_hiddens)#定义神经网络
glove_embedding = d2l.TokenEmbedding('glove.6b.100d')#加载预训练的词向量
embeds = glove_embedding[vocab.idx_to_token]#获取词汇表中每个词元的词向量
net.embedding.weight.data.copy_(embeds)#用预训练的词向量初始化嵌入层
lr, num_epochs = 0.001, 4#定义学习率和迭代周期数
trainer = torch.optim.Adam(net.parameters(), lr=lr)#定义优化器
loss = nn.CrossEntropyLoss(reduction="none")#定义损失函数
d2l.train_ch13(net, train_iter, test_iter, loss, trainer, num_epochs,#训练模型devices)
#@save
def predict_snli(net, vocab, premise, hypothesis):"""预测前提和假设之间的逻辑关系"""net.eval()premise = torch.tensor(vocab[premise], device=d2l.try_gpu())hypothesis = torch.tensor(vocab[hypothesis], device=d2l.try_gpu())label = torch.argmax(net([premise.reshape((1, -1)),hypothesis.reshape((1, -1))]), dim=1)return 'entailment' if label == 0 else 'contradiction' if label == 1 \else 'neutral'
predict_snli(net, vocab, ['he', 'is', 'good', '.'], ['he', 'is', 'bad', '.'])

自然语言推断:微调BERT

自然语言推断(Natural Language Inference, NLI)是一种判断两个句子之间逻辑关系的任务。BERT(Bidirectional Encoder Representations from Transformers)是一种预训练的语言表示模型,它在大量文本数据上进行训练,以学习通用的语言表示。微调(Fine-tuning)是在预训练模型的基础上,针对特定任务进行进一步训练的过程。以下是自然语言推断中微调BERT的原理:

BERT的预训练

在微调之前,BERT模型已经经历了两个预训练任务:

  1. 遮蔽语言模型(Masked Language Model, MLM):随机遮蔽输入序列中的某些 tokens,然后预测这些遮蔽的 tokens。

  2. 下一句预测(Next Sentence Prediction, NSP):给定两个句子,模型预测第二个句子是否是第一个句子的后续句子。

这两个任务使BERT能够学习到丰富的语言表示,这些表示捕获了单词的上下文信息。

具体的示例代码如下:

import json
import multiprocessing
import os
import torch
from torch import nn
from d2l import d2l as d2l
d2l.DATA_HUB['bert.base'] = (d2l.DATA_URL + 'bert.base.torch.zip','225d66f04cae318b841a13d32af3acc165f253ac')
d2l.DATA_HUB['bert.small'] = (d2l.DATA_URL + 'bert.small.torch.zip','c72329e68a732bef0452e4b96a1c341c8910f81f')
def load_pretrained_model(pretrained_model, num_hiddens, ffn_num_hiddens,num_heads, num_layers, dropout, max_len, devices):data_dir = d2l.download_extract(pretrained_model)# 定义空词表以加载预定义词表vocab = d2l.Vocab()vocab.idx_to_token = json.load(open(os.path.join(data_dir,'vocab.json')))vocab.token_to_idx = {token: idx for idx, token in enumerate(vocab.idx_to_token)}bert = d2l.BERTModel(len(vocab), num_hiddens, norm_shape=[256],ffn_num_input=256, ffn_num_hiddens=ffn_num_hiddens,num_heads=4, num_layers=2, dropout=0.2,max_len=max_len, key_size=256, query_size=256,value_size=256, hid_in_features=256,mlm_in_features=256, nsp_in_features=256)# 加载预训练BERT参数bert.load_state_dict(torch.load(os.path.join(data_dir,'pretrained.params')))return bert, vocab
devices = d2l.try_all_gpus()
bert, vocab = load_pretrained_model('bert.small', num_hiddens=256, ffn_num_hiddens=512, num_heads=4,num_layers=2, dropout=0.1, max_len=512, devices=devices)
class SNLIBERTDataset(torch.utils.data.Dataset):#SNLI数据集def __init__(self, dataset, max_len, vocab=None):all_premise_hypothesis_tokens = [[p_tokens, h_tokens] for p_tokens, h_tokens in zip(*[d2l.tokenize([s.lower() for s in sentences])for sentences in dataset[:2]])]
​self.labels = torch.tensor(dataset[2])self.vocab = vocabself.max_len = max_len(self.all_token_ids, self.all_segments,self.valid_lens) = self._preprocess(all_premise_hypothesis_tokens)print('read ' + str(len(self.all_token_ids)) + ' examples')
​def _preprocess(self, all_premise_hypothesis_tokens):pool = multiprocessing.Pool(4)  # 使用4个进程out = pool.map(self._mp_worker, all_premise_hypothesis_tokens)all_token_ids = [token_ids for token_ids, segments, valid_len in out]all_segments = [segments for token_ids, segments, valid_len in out]valid_lens = [valid_len for token_ids, segments, valid_len in out]return (torch.tensor(all_token_ids, dtype=torch.long),torch.tensor(all_segments, dtype=torch.long),torch.tensor(valid_lens))
​def _mp_worker(self, premise_hypothesis_tokens):p_tokens, h_tokens = premise_hypothesis_tokensself._truncate_pair_of_tokens(p_tokens, h_tokens)tokens, segments = d2l.get_tokens_and_segments(p_tokens, h_tokens)token_ids = self.vocab[tokens] + [self.vocab['<pad>']] \* (self.max_len - len(tokens))segments = segments + [0] * (self.max_len - len(segments))valid_len = len(tokens)return token_ids, segments, valid_len
​def _truncate_pair_of_tokens(self, p_tokens, h_tokens):# 为BERT输入中的'<CLS>'、'<SEP>'和'<SEP>'词元保留位置while len(p_tokens) + len(h_tokens) > self.max_len - 3:if len(p_tokens) > len(h_tokens):p_tokens.pop()else:h_tokens.pop()
​def __getitem__(self, idx):return (self.all_token_ids[idx], self.all_segments[idx],self.valid_lens[idx]), self.labels[idx]
​def __len__(self):return len(self.all_token_ids)
# 如果出现显存不足错误,请减少“batch_size”。在原始的BERT模型中,max_len=512
batch_size, max_len, num_workers = 256, 128, d2l.get_dataloader_workers()#批量大小为256,最大长度为128
data_dir = d2l.download_extract('SNLI')#下载SNLI数据集
train_set = SNLIBERTDataset(d2l.read_snli(data_dir, True), max_len, vocab)#加载SNLI数据集
test_set = SNLIBERTDataset(d2l.read_snli(data_dir, False), max_len, vocab)#加载SNLI数据集
train_iter = torch.utils.data.DataLoader(train_set, batch_size, shuffle=True,num_workers=num_workers)
test_iter = torch.utils.data.DataLoader(test_set, batch_size,num_workers=num_workers)
class BERTClassifier(nn.Module):#BERT分类器def __init__(self, bert):super(BERTClassifier, self).__init__()self.encoder = bert.encoderself.hidden = bert.hiddenself.output = nn.Linear(256, 3)
​def forward(self, inputs):tokens_X, segments_X, valid_lens_x = inputsencoded_X = self.encoder(tokens_X, segments_X, valid_lens_x)return self.output(self.hidden(encoded_X[:, 0, :]))
net = BERTClassifier(bert)#定义神经网络
lr, num_epochs = 1e-4, 5
trainer = torch.optim.Adam(net.parameters(), lr=lr)
loss = nn.CrossEntropyLoss(reduction='none')
d2l.train_ch13(net, train_iter, test_iter, loss, trainer, num_epochs,devices)

python

相关文章:

深度学习笔记之自然语言处理(NLP)

深度学习笔记之自然语言处理(NLP) 在行将开学之时&#xff0c;我将开始我的深度学习笔记的自然语言处理部分&#xff0c;这部分内容是在前面基础上开展学习的&#xff0c;且目前我的学习更加倾向于通识。自然语言处理部分将包含《动手学深度学习》这本书的第十四章&#xff0c…...

【ISO 14229-1:2023 UDS诊断全量测试用例清单系列:第十九节】

ISO 14229-1:2023 UDS诊断服务测试用例全解析&#xff08;ClearDiagnosticInformation_0x84服务&#xff09; 作者&#xff1a;车端域控测试工程师 更新日期&#xff1a;2025年02月14日 关键词&#xff1a;UDS协议、0x84服务、清除诊断信息、ISO 14229-1:2023、ECU测试 一、服…...

自动化测试框架搭建-单次接口执行-三部曲

目的 判断接口返回值和提前设置的预期是否一致&#xff0c;从而判断本次测试是否通过 代码步骤设计 第一步&#xff1a;前端调用后端已经写好的POST接口&#xff0c;并传递参数 第二步&#xff1a;后端接收到参数&#xff0c;组装并请求指定接口&#xff0c;保存返回 第三…...

Spring Bean的生命周期和作用域

一、Bean 生命周期 Bean的定义Bean的实例化属性注入Bean的初始化Bean的使用Bean的销毁 可以增强的位置&#xff1a; PostConstruct&#xff1a;属性注入后&#xff0c;afterPropertiesSet方法 &#xff08;前提实现&#xff1a;InitializingBean接口&#xff09;前增强。 Pr…...

DeepSeek R1生成图片总结2(虽然本身是不能直接生成图片,但是可以想办法利用别的工具一起实现)

DeepSeek官网 目前阶段&#xff0c;DeepSeek R1是不能直接生成图片的&#xff0c;但可以通过优化文本后转换为SVG或HTML代码&#xff0c;再保存为图片。另外&#xff0c;Janus-Pro是DeepSeek的多模态模型&#xff0c;支持文生图&#xff0c;但需要本地部署或者使用第三方工具。…...

ESP32 ESP-IDF TFT-LCD(ST7735 128x160) LVGL基本配置和使用

ESP32 ESP-IDF TFT-LCD(ST7735 128x160) LVGL基本配置和使用 &#x1f4cd;项目地址&#xff1a;https://github.com/lvgl/lv_port_esp32参考文章&#xff1a;https://blog.csdn.net/chentuo2000/article/details/126668088https://blog.csdn.net/p1279030826/article/details/…...

数据库连接池与池化思想

目录 1. 数据库连接池概述 1.1 什么是数据库连接池&#xff1f; 1.2 为什么需要连接池&#xff1f; 2. 池化思想 2.1 池化思想的优点 2.2 池化思想的典型应用 3. 常见的开源数据库连接池 3.1 DBCP 3.2 C3P0 3.3 Druid 4. Druid连接池的使用 4.1 Druid的特点 4.2 D…...

深度学习和机器学习的本质区别(白话版)

深度学习与机器学习的本质区别 在人工智能的世界里&#xff0c;机器学习和深度学习是两个常被提及的概念&#xff0c;但它们在本质上有着重要区别。简单来说&#xff0c;机器学习依赖于人为设定的数据模式&#xff0c;而深度学习则更依赖于数据本身自动发现模式。 机器学习&a…...

halcon激光三角测量(十七)calibrate_sheet_of_light_3d_calib_object

目录 一、calibrate_sheet_of_light_3d_calib_object例程代码二、标定过程三、校准后的3D模型和原3D模型对齐过程四、获得模型标定结果&#xff0c;并生成3D模型五、set_paint 和 dev_set_paint函数 一、calibrate_sheet_of_light_3d_calib_object例程代码 1、第一部分&#x…...

【笔记】LLM|Ubuntu22服务器极简本地部署DeepSeek+联网使用方式

2025/02/18说明&#xff1a;2月18日~2月20日是2024年度博客之星投票时间&#xff0c;走过路过可以帮忙点点投票吗&#xff1f;我想要前一百的实体证书&#xff0c;经过我严密的计算只要再拿到60票就稳了。一人可能会有多票&#xff0c;Thanks♪(&#xff65;ω&#xff65;)&am…...

win11 labelme 汉化菜单

替换 app.py,再重启 #labelme 汉化菜单# -*- coding: utf-8 -*-import functools import os import os.path as osp import re import webbrowserimport imgviz from qtpy import QtCore from qtpy.QtCore import Qt from qtpy import QtGui from qtpy import QtWidgetsfrom l…...

Linux的基础指令和环境部署,项目部署实战(下)

目录 上一篇&#xff1a;Linxu的基础指令和环境部署&#xff0c;项目部署实战&#xff08;上&#xff09;-CSDN博客 1. 搭建Java部署环境 1.1 apt apt常用命令 列出所有的软件包 更新软件包数据库 安装软件包 移除软件包 1.2 JDK 1.2.1. 更新 1.2.2. 安装openjdk&am…...

利用Java爬虫精准获取商品SKU详细信息:实战案例指南

在电商领域&#xff0c;SKU&#xff08;Stock Keeping Unit&#xff0c;库存单位&#xff09;详细信息是电商运营的核心数据之一。它不仅包含了商品的规格、价格、库存等关键信息&#xff0c;还直接影响到库存管理、价格策略和市场分析等多个方面。本文将详细介绍如何利用Java爬…...

数值积分:通过复合梯形法计算

在物理学和工程学中&#xff0c;很多问题都可以通过数值积分来求解&#xff0c;特别是当我们无法得到解析解时。数值积分是通过计算积分区间内离散点的函数值来近似积分的结果。在这篇博客中&#xff0c;我将讨论如何使用 复合梯形法 来进行数值积分&#xff0c;并以一个简单的…...

【Java计算机毕业设计】基于SSM+VUE保险公司管理系统数据库源代码+LW文档+开题报告+答辩稿+部署教程+代码讲解

源代码数据库LW文档&#xff08;1万字以上&#xff09;开题报告答辩稿 部署教程代码讲解代码时间修改教程 一、开发工具、运行环境、开发技术 开发工具 1、操作系统&#xff1a;Window操作系统 2、开发工具&#xff1a;IntelliJ IDEA或者Eclipse 3、数据库存储&#xff1a…...

C#之上位机开发---------C#通信库及WPF的简单实践

〇、上位机&#xff0c;分层架构 界面层 要实现的功能&#xff1a; 展示数据 获取数据 发送数据 数据层 要实现的功能&#xff1a; 转换数据 打包数据 存取数据 通信层 要实现的功能&#xff1a; 打开连接 关闭连接 读取数据 写入数据 实体类 作用&#xff1a; 封装数据…...

Pytorch论文实现之GAN-C约束鉴别器训练自己的数据集

简介 简介:这次介绍复现的论文主要是约束判别器的函数空间,作者认为原来的损失函数在优化判别器关于真样本和假样本的相对输出缺乏显式约束,因为在实践中,在优化生成器时,鉴别器对生成样本的输出会增加,但对真实数据保持不变,而优化鉴别器会导致其对真实数据的输出增加…...

vue3.x 的shallowReactive 与 shallowRef 详细解读

在 Vue 3.x 中&#xff0c;shallowReactive 和 shallowRef 是两个用于创建浅层响应式数据的 API。它们与 reactive 和 ref 类似&#xff0c;但在处理嵌套对象时的行为有所不同。以下是它们的详细解读和示例。 1. shallowReactive 作用 shallowReactive 创建一个浅层响应式对…...

MongoDB 常用命令速查表

以下是一份 MongoDB 常用命令速查表&#xff0c;涵盖数据库、集合、文档的增删改查、索引管理、聚合操作等场景&#xff1a; 1. 数据库操作 命令说明show dbs查看所有数据库use <db-name>切换/创建数据库&#xff08;需插入数据后才会显示&#xff09;db.dropDatabase()…...

DeepSeek崛起的本质分析:AI变局中的中国机会

DeepSeek崛起的本质分析&#xff1a;AI变局中的中国机会 1. 中国AI发展的大背景 近年来&#xff0c;全球AI技术竞争日趋白热化&#xff0c;而中国作为全球第二大经济体&#xff0c;在AI领域的投入和政策支持力度不断加大。大模型是AI产业的制高点&#xff0c;而美国对中国的高…...

Autojs: 使用 SQLite

例子 let db new SQLiteUtil("/sdcard/A_My_DB/sqlite.db");db.fastCreateTable("user_table",{name: "",online: false,},["name"] // 设置 name 为唯一, 重复项 不会添加成功 );// 新增数据的 ID let row_id db.insert("use…...

读书笔记 - 修改代码的艺术

读书笔记 - 修改代码的艺术 第 1 章 修改软件第 2 章 带着反馈工作系统变更方式反馈方式遗留代码修改方法 第 3 章 感知和分离伪协作程序模拟对象 第 4 章 接缝模型接缝 第 5 章 工具自动化重构工具单元测试用具 第 6 章 时间紧迫&#xff0c;但必须修改新生方法&#xff08;Sp…...

element-plus树形数据与懒加载的实现

环境 vue版本&#xff1a; 2.6.14 需求 树形表格&#xff0c;默认返回当前登录人拥有权限的一个层级的数据&#xff0c;通过点击load懒加载获取下一层的数据&#xff0c;要求有新增、编辑、删除操作。 树类型的懒加载&#xff1a; 当row中包含children字段时&#xff0c;被…...

仿 Sora 之形,借物理模拟之技绘视频之彩

来自麻省理工学院、斯坦福大学、哥伦比亚大学以及康奈尔大学的研究人员携手开源了一款创新的3D交互视频模型——PhysDreamer&#xff08;以下简称“PD”&#xff09;。PD与OpenAI旗下的Sora相似&#xff0c;能够借助物理模拟技术来生成视频&#xff0c;这意味着PD所生成的视频蕴…...

【算法】快排

题目 快排 思路 如果输入为0或1直接返回&#xff1b;否则取一个基准值&#xff0c;可以取中间位置&#xff0c;如果输入是有序的可以避免时间过长&#xff0c;然后移动指针&#xff0c;先让i指针右移&#xff0c;如果小于基准值就继续右移&#xff0c;j指针左移同理。如果指…...

RedisTemplate存储含有特殊字符解决

ERROR信息: 案发时间: 2025-02-18 01:01 案发现场: UserServiceImpl.java 嫌疑人: stringRedisTemplate.opsForValue().set(SystemConstants.LOGIN_CODE_PREFIX phone, code, Duration.ofMinutes(3L)); // 3分钟过期作案动机: stringRedisTemplate继承了Redistemplate 使用的…...

Django REST Framework (DRF) 中用于构建 API 视图类解析

Django REST Framework (DRF) 提供了丰富的视图类&#xff0c;用于构建 API 视图。这些视图类可以分为以下几类&#xff1a; 1. 基础视图类 这些是 DRF 中最基础的视图类&#xff0c;通常用于实现自定义逻辑。 常用类 APIView&#xff1a; 最基本的视图类&#xff0c;所有其…...

Zotero PDF Translate插件配置百度翻译api

Zotero PDF Translate插件可以使用几种翻译api&#xff0c;虽然谷歌最好用&#xff0c;但是由于众所周知的原因&#xff0c;不稳定。而cnki有字数限制&#xff0c;有道有时也不行。其他的翻译需要申请密钥。本文以百度为例&#xff0c;进行申请 官方有申请教程&#xff1a; Zot…...

Redis离线安装

Linux系统Centos安装部署Redis缓存插件 参考&#xff1a;Redis中文网&#xff1a; https://www.redis.net.cn/ 参考&#xff1a;RPM软件包下载地址&#xff1a; https://rpmfind.net/linux/RPM/index.html http://rpm.pbone.net/ https://mirrors.aliyun.com/centos/7/os…...

五、k8s:容忍 存储卷

容忍&#xff1a; 即使节点上有污点&#xff0c;依然可以部署pod。 tolerations: operator: "Exists" 不指定key&#xff0c;表示容忍所有的污点 cordon和drain cordon: 直接标记节点为不可用&#xff0c;pod不能部署到该节点。新建的pod不会再部署到该节点&#…...