王树森《RNN Transformer》系列公开课

本课程主要介绍NLP相关,包括RNN、LSTM、Attention、Transformer、BERT等模型,以及情感识别、文本生成、机器翻译等应用

ShusenWang的个人空间-ShusenWang个人主页-哔哩哔哩视频 (bilibili.com)


(一)NLP基础 

1、数据处理基础

数值特征(Numeric Features,特点是可以比较大小)和类别特征(Categorical Features)

类别特征需转换成数值特征,但不能只用一个标量表示(因为类别特征做数值运算无意义),需要用one-hot编码

  • Age为numeric feature,可以直接保留,不做处理
  • Gender为binary feature(二元特征),如0表示为女性,1表示为男性
  • Nationality为categorical feature,需编码成数值向量。先转换成1~类别数之间的整数(建立一个字典来映射),再one-hot encoding(因为国籍映射成的整数不能表示大小)【为什么要从1开始?0保留来表示缺失或未知的数据,one-hot后为全0向量
大约有197个国籍

文本处理:

每个单词就是一个类别,单词就是categorical feature,把单词变为数值向量 

  1. Tokenization(Text to Word):把文本(字符串)分割为单词列表
  2. 计算词频:计算每个单词出现的次数(哈希表,Key为word,Value为词频)。若word已经存在在哈希表里,将其value+1;若不存在,添加(word,1)进入哈希表
  3. 排序哈希表:让word按照词频递减的顺序排列
  4. 把词频换为index,词频最高的word的 index=1
  5. 统计词频的目的是:保留常用词,去掉低频词(减小字典里单词的个数,即vocabulary)
  6. one-hot encoding:通过查字典,把每个word映射成一个正整数,再把这个正整数变成one-hot向量(one-hot向量维度=vocabulary)
  7. 在字典里找不到的word,编码时可以忽略这个词,也可以编码成全0向量

为什么要去掉低频词?

一种情况是Name entities(姓名实体,无意义),或拼写错误

另一种原因是不希望vocabulary太大,vocabulary越大,one-hot向量维度越高,会让计算变慢,同时模型参数也会越多,容易过拟合

2、文本处理与词嵌入(Word Embedding)

The IMDB Movie Review Dataset,判断电影评论的情感是正面还是负面(二分类问题) 

5w条电影评论,2w5k作为训练数据,2w5k作为测试数据

Text to Sequence

(1)tokenization(一个token就是一个单词,或一个字符):

  • 通常会将大写转为小写(但Apple若转为apple,语义会发生变化)
  • 去掉停用词(stop words),如:the、a、of等最高频的单词(对二分类没有帮助)
  • 拼写纠错

(2)dictionary:统计词频、去掉低频词;让每个单词对应一个正整数。有了字典,就可以把每个单词映射为一个整数。这样一来,一句话就可以用一个正整数的列表表示,称为sequences序列

(3)one-hot encoding:

(4)align sequences(序列对齐):sequence长度不同,训练数据没有对齐,因为要把数据存储在矩阵或者张量里,序列需要对齐,每条序列有相同的长度

  • 假设序列长度为w,砍掉前面的词,只保留最后w个词(或保留前面w个词也可以)
  • 如果不到w个词,做zero padding,用null来补齐,至长度为w(从前面补齐,或从后面)

总结:一条评论用一个正整数的序列(sequence)来表示,sequence就是神经网络中Embedding层的输入。还需要对齐不同sequence的长度

Word Embedding(Word to Vector)

(1)One-Hot Encoding:字典里一共有v个单词,需要维度为v的one-hot向量(很容易维度过高;RNN的参数数量正比于输入向量的维度

(2)Word Embedding:把高维one-hot向量映射为低维向量(d为词向量的维度,由用户自己决定;v是vocabulary,即字典里单词的数量)

参数矩阵P的每一行都是一个词向量,矩阵的行数是v,每一行对应一个单词;d由用户决定,d的大小会影响机器学习模型的表现,应由cross validation来选择一个比较好的d

参数矩阵是从训练数据中学习出来的

(3)keras提供Embedding层

from keras.models import Sequential
from keras.layers import Flatten, Dense, Embeddingembedding_dim = 8# vocabulary大小 v
# 词向量维度 d (通过cross validation选出)
# 每个Sequence的长度
model.add(Embedding(vocabulary, embedding_dim, input_length=word_num))

Embedding层的输出是一个 (word_num, embedding_dim) 的矩阵,Embedding层的参数数量 = vocabulary × embedding_dim

  • 每条评论中有 word_num 个词,每个单词用 embedding_dim 的词向量来表示
  • Embedding层中有一个参数矩阵P,矩阵P的 行数 = vocabulary,列数 = embedding_dim 

总结:Embedding层把每个单词映射成一个 embedding_dim 的词向量 

Logistic Regression做二分类,判断电影评论是正面还是负面

(1)用keras实现一个分类器:

  • Sequential:把神经网络的层按顺序搭起来,返回model对象,往里依次添加各种层
  • Embedding:输出为 (word_num, embedding_dim) 的矩阵【参数数量 = vocabulary × embedding_dim】
  • Flatten:把 word_num × embedding_dim 的矩阵压扁,变为向量
  • Dense(全连接层,即Logistic Regression):输出是1维的,用sigmoid激活函数,输出为0-1之间的数(0代表负面评价,1代表正面评价)【参数数量 = word_num × embedding_dim + 1,+1是指偏移量bias】
  • summary() 函数可以打印出模型的概要:每一层的名字Layer(type)、输出的大小Output Shape、参数的数量Param #
from keras.models import Sequential
from keras.layers import Flatten, Dense, Embeddingembedding_dim = 8model = Sequential()# vocabulary大小 v
# 词向量维度 d (通过cross validation选出)
# 每个Sequence的长度
model.add(Embedding(vocabulary, embedding_dim, input_length=word_num))
model.add(Flatten())
model.add(Dense(1, activation='sigmoid'))model.summary()  # 打印出模型概要

(2)接下来编译模型:

  • 分为 训练数据train(2w条电影评论) 和 验证数据valid(5k条电影评论)
  • 把train全部扫一遍为一个epoch,每一个epoch都会输出训练loss、acc和验证loss、acc
from keras import optimizersepochs = 50
model.compile(optimizer=optimizers.RMSprop(lr=0.0001), loss='binary_crossentropy', metrics=['acc'])
# 用训练数据来拟合模型
history = model.fit(x_train, y_train, epochs=epochs, batch_size=32, validation_data=(x_valid, y_valid))

x_train是个2w*20的矩阵(20指每条电影评论中有20个单词,每个单词用正整数表示) 

Performance on the training and validation sets

(3)在测试集上检验模型表现:

loss_and_acc = model.evaluate(x_test, labels_test)
print('loss=' + str(loss_and_acc[0]))
print('acc=' + str(loss_and_acc[1]))

电影评论texts,首先做tokenization,变为tokens;

然后把每个tokens编码为一个数字,这样一来,一条电影评论就可以用一个正整数的序列sequence来表示(sequence即神经网络中Embedding层的输入);

由于电影评论的长短不一,得到的sequence长短也不一,故还需要对齐(长度>w,只保留后w个词;长度<w,从前面用null补齐至w)。

输入Embedding层【参数数量 = vocabulary × embedding_dim】,把每个单词映射到一个 embedding_dim 维的词向量;

再用Flatten,将矩阵压扁成向量;

最后用Logistic分类器【参数数量 = word_num × embedding_dim + 1】输出一个0-1之间的数


(二)RNNs(Recurrent Neural Networks)

  • one-to-one模型(一个输入对应一个输出):如全连接神经网络和CNN。适合处理图片(输入一张图片,输出每一类的概率值)
  • many-to-one模型或many-to-many模型(输入和输出长度都不固定):RNN。适合文本、语音等Sequential data(时序序列数据)

1、Simple RNN模型

训练数据足够多时,RNN效果不如Transformer;但在小规模问题,RNN很有用

(1)Simple RNN 

  • 状态向量h:积累阅读过的信息(ht中包含了x0~xt的输入信息)
  • 词向量x:按顺序读取每一个词向量
  • 参数矩阵A:一开始随机初始化,从训练数据中学习

每次把一个词向量输入RNN,RNN就会更新状态h,把新的输入积累到状态h里(h0包含了第一个词的信息,h1包含了前两个词的信息,以此类推)

更新状态h的时候需要用到参数矩阵A(不论链路多长,都只有一个参数矩阵A。A随机初始化,利用训练数据来学习A)

SimpleRNN怎么把输入的词向量x结合到状态h里? 

激活函数是 tanh(双曲正切函数),输入是任意实数,输出在 -1 ~ 1。为什么要用tanh?【每次更新状态h之后,做一个normalization,让h恢复到 -1~1 之间

  • 假设输入的词向量 x0 = ... = x100 = 0,h100 = Ah99 = A² h98 = ... = A^100 h0
  • 若矩阵A最大的特征值<1,新的状态每个元素都趋于0
  • 若矩阵A最大的特征值>1,新的状态每个元素都巨大,状态向量会爆炸(数值为nan=not a number) 
新的状态ht,是旧状态ht-1,和新的输入xt 的函数

可训练参数:参数矩阵A(可能还有intercept vector,即偏置项)

(2)Simple RNN for IMDB Review

  • Word Embedding:词映射为向量x,词向量的维度d由cross-validation确定最优维度【输出维度:(word_num, state_dim)】
  • SimpleRNN Layer:输入是词向量,输出是状态h(维度也由cross-validation确定最优维度)【输出维度:state_dim的向量,若只输出RNN最后一个状态向量ht;(word_num, state_dim),若输出所有状态向量】
  • 可以输出所有h,也可以只输出最后一个状态向量ht(积累了整句话的信息)
  • ht输出分类器,输出0-1之间的数值(0代表负面评价,1代表正面评价)

# (1)搭建模型
from keras.models import Sequential
from keras.layers import SimpleRNN, Embedding, Densevocabulary = 10000   # unique words in the dictionary
embedding_dim = 32    # shape(x)  词向量x的维度
word_num = 500      # sequence length   每个评论长度为500个单词
state_dim = 32     # shape(h)   状态向量h的维度model = Sequential()
model.add(Embedding(vocabulary, embedding_dim, input_length=word_num))   # 词映射为向量
# return_sequences=False 指RNN只输出最后一个状态向量,之前的状态向量全扔掉
model.add(SimpleRNN(state_dim, return_sequences=False))   # 指定状态向量h的维度 state_dim
model.add(Dense(1, activation='sigmoid'))   # 全连接层,输入RNN的最后一个状态h,输出一个0-1之间的数model.summary()# (2)编译模型
from keras import optimizersepochs = 3   # Early stopping防止过拟合(在validation accuracy变差之前就停止)
model.compile(optimizer=optimizers.RMSprop(lr=0.001), loss='binary_crossentropy', metrics=['acc'])
# 用训练数据来拟合模型
history = model.fit(x_train, y_train, epochs=epochs, batch_size=32, validation_data=(x_valid, y_valid))# (3)用测试数据评价模型表现
loss_and_acc = model.evaluate(x_test, labels_test)
print('loss=' + str(loss_and_acc[0]))
print('acc=' + str(loss_and_acc[1]))

RNN层参数数量:shape(h) × (shape(h) + shape(x)) + bias/intercept ,第一项为矩阵A的大小,第二项为RNN默认使用intercept(偏移量)

上述做法是只保留了最后一个状态ht,丢弃了前面所有状态。也可以保留h0~ht,此时RNN输出为一个矩阵(每行就是一个状态h),需要加Flatten层把状态矩阵变成向量。向量作为分类器的输入,来判断电影是正面的还是负面的

from keras.models import Sequential
from keras.layers import SimpleRNN, Embedding, Densevocabulary = 10000   # unique words in the dictionary
embedding_dim = 32    # shape(x)  词向量x的维度
word_num = 500      # sequence length   每个评论长度为500个单词
state_dim = 32     # shape(h)   状态向量h的维度model = Sequential()
model.add(Embedding(vocabulary, embedding_dim, input_length=word_num))   # 词映射为向量
model.add(SimpleRNN(state_dim, return_sequences=True))   # 指定状态向量h的维度 state_dim
model.add(Flatten())
model.add(Dense(1, activation='sigmoid'))   # 全连接层model.summary()

(3)缺点

不擅长long-term dependence:状态h100跟100步之前的输入x1几乎没关系(即后面的状态会遗忘之前的输入)

RNN适合文本、语音等时序序列数据

RNN按顺序读取每一个词向量,并在状态向量h中积累看到过的信息,如h1中包含x0和x1的信息,以此类推,ht中包含了之前所有输入的信息,可以认为,ht就是RNN从整个输入序列中抽取的特征向量

RNN记忆很短,会遗忘很久之前的输入x。若时间序列很长,比如好几十步,最终的ht已经忘了早先的输入

SimpleRNN有一个参数矩阵A,维度是 shape(h) × (shape(h)+shape(x)),一开始随机初始化,从训练数据中学习。可能还有一个intercept向量

注意,不管时序多长,参数矩阵只有一个,所有模块里的参数都是一样的

2、LSTM(Long Short Term Memory)

LSTM的记忆会比SimpleRNN长很多,但也还是有遗忘的问题。LSTM是一种RNN模型,可以避免梯度消失的问题,可以有更长的记忆(一般用RNN,都是LSTM,SimpleRNN基本不用) 

  • 每当读取一个新的输入x,就会更新状态h
  • SimpleRNN只有一个参数矩阵,LSTM有四个参数矩阵
    • 遗忘门有一个参数矩阵Wf(sigmoid映射到0~1)
    • 输入门有两个参数矩阵:Wi(sigmoid映射到0~1)、Wc(tanh映射到 -1~1)
    • 输出门有一个参数矩阵Wo(sigmoid映射到0~1)

(1)内部结构 

传输带Ct:过去的信息直接送到下一个时刻,以此避免梯度消失

LSTM中有很多个gate(遗忘门、输入门、输出门),可以有选择地让信息通过

  • forget gate(遗忘门):由 sigmoid函数 和 element-wise multiplication/哈达玛积(两个向量的每个元素对应相乘,结果也是个向量)组成。有选择地让传输带C的值通过(假如f 对应的元素为0,c对应的元素就不能通过,对应的输出为0;假如f 对应的元素为1,c对应的元素就全部通过,对应的输出为c本身)

遗忘门f有选择的让传输带c的元素通过

遗忘门有一个参数矩阵Wf,需要通过反向传播从训练数据里学习 

  • input gate(输入门):参数矩阵Wi(sigmoid映射到0~1)、参数矩阵Wc(tanh映射到 -1~1)

new value

更新传输带C:

  • 用 遗忘门ft 和 传输带旧的值Ct-1 算element-wise multiplication(遗忘门ft 可以选择性地遗忘 Ct-1中的一些元素)
  • 计算 输入门it 和 新的值Ct 的element-wise multiplication(加入新的信息)

  • output gate(输出门):计算ot,参数矩阵Wo(sigmoid函数映射到0~1)

计算状态向量ht:一份传到下一步,另一份是LSTM的输出

到第t步为止,一共有t个向量x输入了LSTM,可以认为所有这些x向量的信息都积累在了状态ht里

(2)参数数量

遗忘门、输入门、new value、输出门,共有4个参数矩阵,共有 4 × shape(h) × [shape(h) + shape(x)]

  • 矩阵的行数:shape(h)
  • 列数:shape(h) + shape(x)

(3)keras实现LSTM

让LSTM只输出最后一个状态向量ht,即从电影评论中提取出的特征向量,再输入线性分类器,来判断评论是正面的还是负面的

from keras.models import Sequential
from keras.layers import LSTM, Embedding, Dense, Flattenvocabulary = 10000   # unique words in the dictionary
embedding_dim = 32    # shape(x)  词向量x的维度
word_num = 500      # sequence length   每个评论长度为500个单词
state_dim = 32     # shape(h)   状态向量h的维度model = Sequential()
model.add(Embedding(vocabulary, embedding_dim, input_length=word_num))   # 词映射为向量
# return_sequences=False 指RNN只输出最后一个状态向量,之前的状态向量全扔掉
model.add(LSTM(state_dim, return_sequences=False))   # 指定状态向量h的维度 state_dim
model.add(Dense(1, activation='sigmoid'))   # 全连接层model.summary()
只输出最后一个状态向量h
  • 每个参数矩阵:shape(h) × [shape(h) + shape(x)] + shape(h)(LSTM默认使用intercept)
  • LSTM参数量:*4

可以加dropout(设置为某个0-1之间的数字即可):

model.add(LSTM(state_dim, return_sequences=False), dropout=0.2)

若加dropout没有提升测试准确率,原因:虽然训练时出现了overfitting,但overfitting不是由LSTM造成的,而是由Embedding层造成的,故对LSTM使用dropout正则化没有用

LSTM和SimpleRNN的区别是用了一条传输带,让过去的信息可以很容易的传输到下一时刻,这样就有了更长的记忆

LSTM有4个组件,分别是:forget gate(遗忘门)、input gate(输入门)、new value(新的输入)、output gate(输出门),这4个组件各自有一个参数矩阵,所以一共有4个参数矩阵,参数数量为 4 × shape(h) × [shape(h) + shape(x)]

3、Making RNNs More Effective

三个技巧来提升RNN的效果(对所有RNN都适用)

(1)Stacked RNN(多层RNN)

  • 把很多全连接层堆叠起来:multi-layer perceptron
  • 把很多卷积层堆叠起来:深度卷积网络
  • 把很多RNN层堆叠起来:多层RNN网络

神经网络每一步都会更新状态h,有两份:一份送到下一时刻,一份作为输出(同时也是下一层的输入)

# 多层LSTM 用keras实现
from keras.models import Sequential
from keras.layers import LSTM, Embedding, Densevocabulary = 10000   # unique words in the dictionary
embedding_dim = 32    # shape(x)  词向量x的维度
word_num = 500      # sequence length   每个评论长度为500个单词
state_dim = 32     # shape(h)   状态向量h的维度model = Sequential()
model.add(Embedding(vocabulary, embedding_dim, input_length=word_num))   # 词映射为向量
# return_sequences=True 第一层的输出会成为第二层的输入,故要输出所有的状态向量h
model.add(LSTM(state_dim, return_sequences=True, dropout=0.2))   # 指定状态向量h的维度 state_dim
model.add(LSTM(state_dim, return_sequences=True, dropout=0.2)) 
model.add(LSTM(state_dim, return_sequences=False, dropout=0.2))   # 只输出最后一个状态向量
model.add(Dense(1, activation='sigmoid'))   # 全连接层,输入第三层LSTM最后一个状态向量,输出分类结果

  • Embedding层输出:(word_num,  embedding_dim)
  • 第一层LSTM输出:(word_num, state_dim)  return_sequences=True(输出500个状态向量h)
  • 第二层LSTM输出:(word_num, state_dim)  return_sequences=True(输出500个状态向量h)
  • 第三层LSTM输出:state_dim维的向量     return_sequences=False(最后一个状态,相当于从word_num个词里提取的特征向量)

实验结果跟单层RNN效果差不多,猜想是由于Embedding层参数太多,没有足够的数据把这一层训练好,出现overfitting,加再多LSTM层也无济于事

(2)Bidirectional RNN(双向RNN)

训练两条RNN,一条从左往右,一条从右往左,两条RNN完全独立,不共享参数和状态。两条RNN各自输出自己的状态向量,然后把它们的状态向量做concat,记为向量y

  • 如果有多层RNN,就把输出的向量y作为下一层RNN的输入

  • 如果只有一层RNN,就把y向量都丢掉,只保留两条RNN最后的状态向量,把它们concat,作为从输入文字中抽取的特征向量,以此来判断电影评论是正面还是负面

双向RNN总是比单向的效果好,原因:不管是SimpleRNN还是LSTM,都会或多或少遗忘掉早先的输入。而双向RNN左右结合,就不会遗忘一开始的词

# 双向LSTM 用keras实现
from keras.models import Sequential
from keras.layers import LSTM, Embedding, Dense, Bidirectionalvocabulary = 10000   # unique words in the dictionary
embedding_dim = 32    # shape(x)  词向量x的维度
word_num = 500      # sequence length   每个评论长度为500个单词
state_dim = 32     # shape(h)   状态向量h的维度model = Sequential()
model.add(Embedding(vocabulary, embedding_dim, input_length=word_num))   # 词映射为向量
# return_sequences=False  只保留两条链最后的状态,输出两个状态向量的concat,其余状态向量都被扔掉了
model.add(Bidirectional(LSTM(state_dim, return_sequences=False, dropout=0.2)))   # 指定状态向量h的维度 state_dim
model.add(Dense(1, activation='sigmoid'))   # 全连接层model.summary()
  • Embedding层输出:(word_num,  embedding_dim)
  • 双层RNN输出:(state_dim×2) 维的向量   return_sequences=False(输出两条链最后的状态向量)【参数数量比使用单向LSTM多一倍,因为两条链各自有各自的模型参数】

(3)pretrain(预训练)

比如在训练卷积神经网络时,如果网络太大,而训练集不够大,可以先在ImageNet等大数据上预训练,这样可以让神经网络比较好的初始化,也可以避免overfitting

若Embedding层参数>>训练样本数量,会导致overfitting。可以对Embedding层做预训练

  • 首先找一个更大的数据集(可以是情感分析数据或其他类型数据,但任务最好是接近情感分析任务,学出的词向量带正面或负面的情感。两个任务越相似,预训练后的transfer越好)
  • 搭建一个神经网络(有Embedding层即可),在大数据集上训练该神经网络
  • 训练完毕后,把上面的层全部丢掉,只保留Embedding层和训练好的模型参数
  • 再搭建自己的RNN网络(跟之前预训练的可以有不同的结构),新的RNN层和全连接层都是随机初始化,而Embedding层的参数是预训练出来的(固定住,不要训练)

总结循环神经网络RNN:

SimpleRNN和LSTM都属于RNN

(1)SimpleRNN很容易遗忘,效果不好,实践中不用

(2)LSTM的记忆比SimpleRNN长很多,实践中都用LSTM(还有GRU,但是效果不如LSTM)

(1)双向LSTM效果比单向好

(2)RNN层可以像全连接层和卷积层那样累加起来,搭成一个深度神经网络。多层RNN容量比单层RNN更大,如果训练数据够多,多层RNN效果更好

(3)想把RNN用在文本问题上,需要有一个Embedding层把词变成向量,Embedding层有一个参数矩阵(大小是vocabulary×词向量的维度)。这个参数矩阵通常很大,若训练数据集比较小,Embedding层就不会训练的很好,会overfitting。解决办法是在大数据集上预训练Embedding Layer

4、RNN的应用 — 自动文本生成(Text Generation)

(1)技术原理

输入半句话,预测 input text 的下一个字符。拿训练好的RNN来生成文本:

  • 把文本分割成字符,用one-hot encoding来表示字符,这样,每个字符就表示成一个one-hot向量
  • 把这些one-hot向量依次输入RNN,RNN状态向量h会积累看到的信息。返回最后一个状态向量h
  • RNN后是一个softmax分类器,把h与参数矩阵W相乘,得到一个向量。经过softmax函数的变换,最终输出是一个向量,每个元素都在0-1之间,元素全加起来=1(softmax输出是一个概率分布)
  • 选择概率值最大的字符,接到文本末尾,作为新的输入,生成下一个字符,重复这个过程

如何训练这个RNN?
  • 训练数据:文本,如英文维基百科的所有文章。把文章划分成很多片段(可以有重叠overlap)
    • seg_len = 40(片段长度),stride = 3(下一个片段会向右平移3个字符的长度)
    • 片段是神经网络的输入,片段的下一个字符是标签,训练数据是 (片段, 标签) 的pairs
红色是片段,蓝色是标签
  • 多分类问题,每个类别对应一个概率值
  • 文本生成器并不是记住训练数据并重复,而是可以生成新的东西

(2)训练一个文本生成器

想要生成文本,首先需要训练一个RNN 

  • 准备训练数据:将训练文本划分为 (segment, next_char) 的 pairs。segment是神经网络的输入,next_char是标签

划分segment 和 next_char: 

  • 字符->one-hot向量:故片段->矩阵(之前还需要进一步做word embedding,用一个低维词向量来表示一个词。这里不需要embedding层,因为之前是word-level tokenization,英语里约有1w个常用词,one-hot向量都是1w维,维度太高;而char-level tokenization把一句话切成很多个字符,常用字符大概是100个=字母+数字+标点+空白)

len(text) = 600893 / stride = 3 约等于 200278 pairs 

  • 搭建神经网络:输入是segment(l×v的矩阵,l是每个segment的长度,即有l个字符;v是vocabulary,是字典里不同字符的数量)——> 单向LSTM(注意只能用单向LSTM,因为文本生成的下一个字符必须是从前往后)——>全连接层(用softmax激活函数,多分类器)——> 输出v×1的向量(向量的每个元素是一个字符的概率)

  • 编译模型:指定损失函数(CrossEntropy)和优化器(RMSProp),用训练数据拟合模型,训练几十个epochs

  • 训练好神经网络就可以生成文本,即预测下一个字符。首先需要给出seed segment,神经网络会接着你的输入生成文本。输出一个向量代表每个字符的概率值

  • 有了概率分布,如何生成下一个字符?三种方法:
greedy selection,哪个字符的概率最大,就选择哪个字符确定性的,没有随机性。给定初始的几个字符,后面生成的字符完全是确定的(完全取决于初始输入)
从多项式分布中随机抽取。假如一个字符的概率值是0.3,那么它被选中的概率就是0.3抽样过于随机,生成的文本会有很多拼写和语法错误
用介于0~1之间的temperature调整概率值:把概率值做幂变换,再归一化(大的概率值会变大,小的会变小。极端情况下,最大的概率值会变为1,其余都变为0,就相当于第一种确定性的选择)有随机性,但随机性不大,介于前两者之间。temperature越小,变换后的概率分布越极端
# greedy selection
next_index = np.argmax(pred)# sample from the multinomial distribution
next_onehot = np.random.multinomial(1, pred, 1)
next_index = np.argmax(next_onehot)# adjust the multinomial distribution
pred = pred ** (1/temperature)   # controlled temperature
pred = pred/np.sum(pred)
temperature越小,变换后的概率分布越极端
神经网络怎样做文本生成?
  • 假设固定每个片段的长度为18个字符,最初的片段为seed(做one-hot变为矩阵),把矩阵输入神经网络,神经网络就会输出概率分布,从概率分布中抽样生成下一个字符
  • 把新生成的字符加到最后,作为下一轮的输入,输入的长度固定为18
  • 以此类推

文本生成是随机的,所以每次生成的都不一样 

训练一个神经网络:

  • 将文本划分为 (segment, next_char) pairs
  • one-hot:
    • char —> v×1 vector
    • segment —> l×v matrix
  • 构建+训练神经网络:l×v矩阵 —> LSTM —> Dense —> v×1 vector

文本生成:

  • 输入一个seed segment
  • 重复以下:
    • 将one-hot后的segment输入神经网络
    • 神经网络输出概率值
    • 从概率值中采样生成next_char
    • 将next_char append到segment后

5、RNN的应用 — 机器翻译(Neural Machine Translation)

机器翻译模型有很多种,这里介绍Seq2Seq(例:英译德)

机器翻译是个Many to Many的问题,输入、输出长度都大于1且不固定

(1)处理数据

给定一句英语,如果翻译结果能match其中一个德语句子,就算翻译正确
  • 预处理:大写字母变为小写,去掉标点符号等
  • tokenization(可以是char-level或word-level,实际机器翻译都是word-level,因为数据量够大):要用两个不同的tokenizer(英语一个德语一个),并建立两个不同的字典(因为不同的语言通常有不同的字母表,且分词方法也不同)

例子里用的是char-level(比较方便,不用Embedding层),但最好用word-level前提是需要有足够大的数据集)。原因:

  • 英文平均每个单词有4.5个字母,用单词代替字符,输入序列就会短4.5倍。序列越短,越不容易遗忘
  • word-level得到的Vocabulary大约为1w(也是one-hot的维度),必须要用word Embedding得到低维词向量(Embedding层参数数量太大,小数据量无法训练,会有overfitting的问题;或对Embedding层做预训练)
  • 英译德,故德语的字典里需要加入 起始符\t 和 终止符\n

  • 此时每句话都变成了一个字符的列表,并有一个英文字典和一个德语字典,再把每个字符映射为一个数字
  • 再把每个数字做one-hot,得到一个矩阵,这个矩阵就是RNN的输入

(2)训练Seq2Seq模型

Seq2Seq有一个Encoder编码器(是个LSTM或其他RNN模型,用来从输入的句子中提取特征)和一个Decoder解码器(用来生成德语,就是文本生成器)

  • Encoder的最后一个状态就是从输入句子中提取的特征,包含这句话的信息。其余状态都被丢弃了。Encoder的输出是LSTM最后一个状态h 以及 最后的传输带c
  • Decoder跟文本生成器的区别是,文本生成器的初始状态是个全零向量,而Decoder初始状态是Encoder最后一个状态(从而得知输入的英语句子)
  • Decoder是一个LSTM,每次接收一个输入,输出对下一个字符的预测(输出一个概率分布向量p),第一个输入必须是起始符\t,将起始符后的第一个字符one-hot后作为label。损失函数为CrossEntropy
  • 最后一轮:整句德语作为Decoder输入,label为停止符\n

(3)用训练好的模型inference

 

最后一轮:

  • 每一轮会更新状态(h, c),并输出一个概率分布
  • 用新生成的字符作为下一轮的输入
  • 输出终止符\n时终止文本生成,并返回记录下的字符串,即模型翻译得到的德语 

用Seq2Seq做机器翻译:

模型有一个Encoder(每输入一个词就更新状态,把输入信息积累在状态里。最后一个状态就是从英文句子里积累的特征。只保留最后一个状态)和一个Decoder(Encoder的最后一个状态是Decoder的初始状态,初始化后Decoder就知道输入的英文句子了;然后Decoder就作为文本生成器,生成一句德语:首先把起始符\t作为Decoder RNN的输入,会更新状态为s1,全连接层输出预测概率为p1,根据概率分布做抽样生成下一个字符为z1;Decoder拿z1做输入,更新状态为s2,输出概率p2,得到新的字符z2,以此类推,直到输出停止符\n)

(4)怎么提升Seq2Seq?

Seq2Seq的原理是Encoder处理输入的英语句子,把信息都压缩到状态向量里,最后一个状态是整句话的概要(包含整句英语的完整信息)。但若英语句子很长,早期的输入就会被遗忘

四种改进方法:

  • Encoder用双向LSTM,但Decoder必须用单向(文本生成器必须按顺序生成文本)

  • 做word-level tokenization而不是char-level

  • Multi-task Learning(添加更多任务,等同于添加更多Decoder,注意Encoder只有一个):
    • 如添加一个Decoder把英语翻译为英语本身。这样一来,Encoder还是只有一个,但训练数据多了一倍,Encoder可以训练的更好
    • 或添加任务将英文翻译为其他语言。如用十种语言训练,Encoder的训练数据就多了十倍,可以训练的更好。即借助其他语言使Encoder变得更好
英语->英语
英语->其他语言
  • Attention

评估机器翻译的效果可以用BLEU(BiLingual Evaluation Understudy)指标,范围应该在0.1~0.5

  • Wikipedia:https://en.wikipedia.org/wiki/BLEU
  • Blog:A Gentle Introduction to Calculating the BLEU Score for Text in Python - MachineLearningMastery.com

(三)注意力

1、Attention(注意力机制)

(1)回顾Seq2Seq

有两个RNN网络,一个编码器Encoder(输入英语)和一个解码器Decoder(把英语翻译成德语)

  • Encoder每次读入一个英语词向量x,在状态h中积累输入的信息,最后一个状态hm中积累了所有词向量x的信息。Encoder输出最后一个状态hm,把之前的状态向量全都扔掉
  • Decoder初始状态s0=hm(包含了输入英语句子的信息),通过hm,Decoder就知道了这句英语。Decoder类似文本生成器,逐字生成一句德语(即模型生成的翻译)

缺陷:若输入句子很长,Encoder会记不住完整的句子,那么Decoder也就不可能产生正确的翻译

BLEU score是评价机器翻译好坏的标准,越高说明机器翻译越准确

(2)用Attention改进Seq2Seq 

解决Seq2Seq遗忘问题最有效的方法:Attention(Decoder每次更新状态的时候,都会再看一遍Encoder所有状态,这样就不会遗忘;Attention还会告诉Decoder应该关注Encoder哪个状态) 

Attention可以大幅提高准确率,但计算量较大

在Encoder结束工作后,Attention和Decoder同时开始工作

Encoder的所有状态都要保留,并计算s0与每个状态的相关性α(也叫权重,介于0~1,求和为1)

计算 hi 和 s0 的相关性,有2种方法:

(1)原论文提出:

tanh把每一个元素都压到 -1~1

(2)更常用,同Transformer:

  • 每一个 权重αi 对应一个 Encoder状态hi
  • 对 α 和 h 做加权平均,得到向量c(Context Vector)
  • 每一个 Context Vector ci 对应一个 Decoder状态si

c0是Encoder所有状态的加权平均,故c0知道Encoder输入x1~xm的完整信息;

Decoder新状态s1依赖于c0,故Decoder也知道Encoder的完整输入,解决了RNN遗忘的问题

Attention的时间复杂度(也是weights的数量):Encoder 和 Decoder 状态数量的乘积

可视化:

每当Decoder想要生成一个状态时,都会看一遍Encoder的所有状态,同时权重weights会告诉Decoder要关注Encoder的哪个状态 

Seq2Seq:Decoder基于当前状态来生成下一个状态,这样产生的新状态可能已经忘了Encoder的部分输入

Attention:Decoder在产生下一个状态之前,会先看一遍Encoder的所有状态,于是Decoder就知道Encoder的完整信息,并不会遗忘;除此之外,还能告诉Decoder应该关注Encoder的哪个状态

Attention可以大幅提升Seq2Seq模型的表现,缺点是计算量太大

假设输入Encoder的序列长度为m,Decoder输出序列长度为t

Seq2Seq:只需要Encoder读一遍输入序列,之后不会再看Encoder的输入或状态;Decoder依次生成输出序列,时间复杂度O(m+t)

Attention:Decoder每次更新状态,都要把Encoder的m个状态都看一遍,Decoder又有t个状态,故时间复杂度为O(mt)

2、Self-Attention

Attention用在Seq2Seq上,Seq2Seq有2个RNN网络(一个Encoder一个Decoder)

而Self-Attention是把Attention用在一个RNN网络上

SimpleRNN + Self-Attention

初始状态向量h0 和 Context Vector c0 都为全零向量

RNN读入第一个输入x1,需要更新状态h1:

计算新的Context Vector c1:是已有状态h的加权平均(由于初始状态h0是全零向量,故忽略h0,此时c1=h1)

计算新的状态h2:

计算新的Context Vector c2:

以此类推

初始状态向量h0 和 Context Vector c0 都为全零向量

重复以下步骤:

  • 读入向量xi
  • 用 xi 与 ci-1 计算出新的状态hi:hi = tanh(A·[xi ci-1]^T + b)
  • 拿当前状态hi与h1~hi(h0为全零向量,不考虑)作对比,计算权重α1~αi
  • 计算i个状态向量h的加权平均,得到新的context vector ci

RNN都有遗忘的问题,Self-Attention可以解决RNN遗忘的问题(每一轮更新状态之前,都会用Context Vector c看一遍之前所有的状态,这样就不会遗忘之前的信息了)

Self-Attention不局限于Seq2Seq模型,可以用在所有RNN上

除了避免遗忘,Self-Attention还能帮助RNN关注相关的信息

RNN从左往右读一句话,红色是当前输入,高亮是权重很大的位置(说明前文中最相关的词是什么) 

(四)Transformer(=Attention without RNN)

1、剥离RNN,保留Attention

  • Transformer是一种Seq2Seq模型(Encoder & Decoder,适合做机器翻译)
  • Transformer不是循环神经网络RNN,没有循环的结构,只有Attention和全连接层
  • 在大数据集上,Transformer的accuracy显著高于RNN

(1)Attention for Seq2Seq Model

i是Encoder状态h的下标,j是Decoder状态s的下标

计算过程: 

Attention中一共有3个参数矩阵:

Transformer里用的:

(2)Attention without RNN

Transformer就是由Attention层(Seq2Seq)和Self-Attention层组成的

一共有3个参数矩阵,Encoder中有K和V,Decoder中有Q

  • Encoder的input:x1~xm(生成Key和Value)
  • Decoder的input:x1'~xt'(生成Query)

如英译德,英语里有m个词变为词向量(即x1~xm),把当前生成的德语单词作为下一轮的输入: 

Attention与RNN做机器翻译的不同在于:

  • RNN会把状态h作为特征向量输入softmax
  • 而Attention是把Context Vector c作为特征向量(可以用Attention Layer代替RNN,它不会遗忘)

Attention层:有两个输入序列X和X',有一个输出序列C,每个c向量对应一个x'向量

(3)Self-Attention without RNN

  • Attention用于Seq2Seq,有2个输入序列(如英译德,英文一个输入序列,德语一个输入序列)
  • Self-Attention不是Seq2Seq,它只有一个输入序列,其他跟Attention完全一样

以此类推计算得到其他α

以此类推计算得到其他c 

ci 并非只依赖于 xi,而是依赖于所有m个x(改变任何一个x,输出的ci都会发生变化)

Attention最初提出是用在Seq2Seq模型,但Attention不局限于Seq2Seq,而是可以用在所有RNN上

若只有一个RNN网络,Attention就是Self-Attention

不用RNN,只用Attention,就是Transformer

用于Seq2Seq,可以做机器翻译,输入是两个序列
输入只有1个序列,输出的c向量类比于RNN输出的状态向量。Single-Head Self-Attention

(4)Multi-Head Self-Attention

由 l 个单头组成(不共享参数),每个单头有3个参数矩阵,故多头共有 3l 个参数矩阵

所有单头Self-Attention都有相同的输入x1~xm序列,但它们的参数矩阵各不相同,故输出的c序列也各不相同。把 l 个单头的输出(d×m)堆叠起来,作为多头的输出(ld×m)

(5)Multi-Head Attention

所有单头Attention的输入都是两个序列x1~xm以及x1'~xt'

每个单头Attention都有各自的参数矩阵(不共享参数)

每个单头都有自己的输出序列c,把单头输出的序列c堆叠起来,就是多头的输出

2、从Attention层到Transformer网络

(1)Stacked Self-Attention Layers

输入x1~xm,输出u1~um。但ui依赖于x1~xm,而不是仅仅依赖于xi

Transformer Encoder
  • 6个blocks,每个block有自己的参数,不共享
  • 每个Block有2层——Self-Attention Layer + Dense Layer
  • 输入和输出都是512×m的矩阵(m是输入序列x的长度,每个x向量都是512维),故可以用ResNet的Skip Connection方式,把输入加到输出上

(2)Stacked Attention Layers

Transformer Decoder
  • 一个block有3层:Self-Attention层、Attention层、全连接层 
  • x1'~xt' 以及 c 以及 z 都是512维的向量

Decoder的一个Block如图所示,需要两个输入序列,输出一个序列 

(3)Transformer

Encoder:

  • 6个Blocks,每个Block有2层
  • 输入有m列,每列都是512维的词向量,输出维度同输入

Decoder:

  • 6个Blocks,每个Block有3层:Self-Attention、Attention、全连接层
  • 每个Block有两个输入序列(Encoder网络的输出+上一个Decoder Block的输出),一个输出序列(t个向量,每个向量都是512维)

3、对比RNN Seq2Seq

两者输入、输出大小完全一样:

  • RNN Seq2Seq有两个输入序列(Encoder:x1~xm,Decoder:x1'~xt'),Transformer同
  • RNN Seq2Seq有一个输出序列s1~st(Decoder输出),Transformer同
m是输入序列的长度

4、Example:英译德 

Encoder:有6个block(block之间不共享参数,block之间还有skip-connection的技巧),每个block = 多头self-attention + dense,每个block的输入、输出都是512×m(m是输入序列的长度)

Decoder:有6个block(block之间不共享参数,block之间还有skip-connection的技巧),每个block = 多头self-attention + 多头attention + dense,每个block的输入是两个序列:(512×m,512×t),输出一个序列512×t

Transformer:

  • Seq2Seq模型,有Encoder和Decoder,可以用来做机器翻译;
  • 不是RNN,无循环结构;
  • 完全基于Attention和Self-Attention和全连接层;
  • 和RNN的输入、输出大小一样

(五)Bert(Bidirectional Encoder Representations from Transformers)

一种用来预训练Transformer Encoder网络的方法,从而大幅提高准确率。有以下2个任务:

  • 随机遮挡一个或多个单词,让Encoder网络根据上下文来预测被遮挡的单词
  • 两个句子放在一起,让Encoder网络判断两句话是不是原文里相邻的两句话

1、预测被遮挡的单词

Um不仅依赖于Xm,而是依赖于所有X向量。即 Um在 [MASK] 位置上,但它包含整句话的上下文信息 

用反向传播算出损失函数关于模型参数的梯度,然后做梯度下降来更新模型参数 

  • Bert会随机遮挡单词,把遮住的单词作为标签;
  • Bert预训练不需要人工标注的数据集,可以自动生成标签
  • 多分类

2、预测下一个句子

二分类:0代表False(两句话不相邻),1代表True

例1:

例2: 

50%是确实相邻的两句话(标签True),还有50%的第二句话是随机抽取的(标签False)

向量c在[CLS]位置上,但它包含两句话的全部信息,所以靠向量c就能判断两句话是否真实相邻

这样做预训练有什么用呢?

  • 相邻两句话通常有关联,这样做二分类可以强化这种关联,让Embedding和词向量包含这种关联
  • Encoder网络里有Self-Attention层(作用:找相关性),这种分类任务可以训练Self-Attention找到正确的相关性

3、结合两个任务

把两句话拼接起来,并随机遮挡15%的单词

目标函数是多个损失函数的加和。把目标函数关于模型参数求梯度,然后做梯度下降来更新模型参数

4、Bert的特点

  • 不需要人工标注数据,两种任务的标签都是自动生成的
  • 计算代价大

  • 训练好的模型参数是公开的

bert可以利用海量数据来训练一个超级大的模型

bert的Embedding层不是简单的word embedding,还有一些技巧


(六)ViT(Vision Transformer)

Dosovitskiy. An image is worth 16×16 words: transformers for image recognition at scale. In ICLR.

  • Transformer模型在图片分类(自动判断图片中的物体是什么)上的应用
  • 目前图片分类最好的模型,超越了最好的CNN(ResNet),前提是要在足够大的数据集上做预训练。数据集越大,ViT优势越明显
向量p是分类结果,p的每个元素对应一个类别,大小介于0~1,且相加为1

1、ViT

就是Transformer Encoder网络

(1)Split Image into Patches

把图片划分为大小相同的patches(可以有重叠overlap,也可以没有)

  • 用一个滑动窗口,每次移动若干个像素
  • 每次移动的步长叫stride(stride越小,得到的patches越多,计算量越大)
如划分为9块patches

对图片划分的时候,需要指定两个超参数:

  • patch size:每块patch的大小,如16×16
  • stride:滑动窗口移动的步长,如16×16(=patch size,无overlap)

(2)Vectorization向量化

每个小块都是一张彩色图片,有RGB三通道,即每个小块都是一个张量

把张量拉伸为向量:

设图片被划分为了n块,变为了n个向量,首先用全连接层对向量x做线性变换

  • 此处全连接层不使用激活函数,只是线性函数
  • W和b是参数,从训练数据中学习,且对全连接层是共享的

(3)Positional Encoding

对图片每一块的位置做编码

  • 图片被分为n块,那么位置就是1~n之间的整数,每个位置被编码为一个向量,向量大小跟z向量相同
  • 把位置编码向量加到z向量上,这样一来,一个z向量既是patch内容的表征,也包含patch的位置信息

为什么要用PE?

  • 如果不用,会掉3个百分点的准确率
  • 不同的PE方式表现几乎一样,用什么样的PE影响不大
  • 若z向量里不包含位置信息,那么以下两张图在Transformer眼里是一样的

(4)网络结构

  • x1~xn是图片中n个小块向量化得到的结果
  • 对x1~xn做线性变换,并加入位置信息,得到z1~zn为图片n个小块的表征(既包含内容信息,又包含位置信息)
  • [CLS] 表示分类,对这个符号做Embedding,得到向量z0(跟其他z向量大小相同),这个输出被用作分类

  • Transformer里有skip connection,把每一层的输入加到输出上
  • 还有BN的技巧 

输出n+1个向量c,其中c1~cn没有用,c0可以看做是从图片中提取的特征向量,用作分类任务 

向量p的大小为类别的数量

2、训练

随机初始化神经网络参数 -> 在大数据集A(如JFT,三亿张图片)上做预训练 -> 在小数据集B(任务/目标数据集,如ImageNet图片分类,30w张图片)训练集上做微调 -> 在数据集B测试集上评价模型表现,得到测试准确率

  • 当预训练数据集不够大时,ViT表现不好
  • 用越大的数据集(超过1亿张)做预训练,ViT效果越好(比ResNet高1个百分点)
  • 预训练数据量1亿或3亿,对于ResNet来说区别不大

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/681380.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring Boot 笔记 007 创建接口_登录

1.1 登录接口需求 1.2 JWT令牌 1.2.1 JWT原理 1.2.2 引入JWT坐标 1.2.3 单元测试 1.2.3.1 引入springboot单元测试坐标 1.2.3.2 在单元测试文件夹中创建测试类 1.2.3.3 运行测试类中的生成和解析方法 package com.geji;import com.auth0.jwt.JWT; import com.auth0.jwt.JWTV…

【Spring】公司为什么禁止在SpringBoot项目中使用@Autowired注解

目录 前言 说明 依赖注入的类型 2.1 基于构造器的依赖注入 2.2 基于 Setter 的依赖注入 2.3 基于属性的依赖注入 基于字段的依赖注入缺陷 3.1 不允许声明不可变域 3.2 容易违反单一职责设计原则 3.3 与依赖注入容器紧密耦合 3.4 隐藏依赖关系 总结 参考文档 前言 …

网络协议与攻击模拟_16HTTP协议

1、HTTP协议结构 2、在Windows server去搭建web扫描器 3、分析HTTP协议流量 一、HTTP协议 1、概念 HTTP&#xff08;超文本传输协议&#xff09;用于在万维网服务器上传输超文本&#xff08;HTML&#xff09;到本地浏览器的传输协议 基于TCP/IP(HTML文件、图片、查询结构等&…

数字IC实践项目(9)— Tang Nano 20K: I2C OLED Driver

Tang Nano 20K: I2C OLED Driver 写在前面的话硬件模块RTL电路和相关资源报告SSD1306 OLED 驱动芯片SSD1306 I2C协议接口OLED 驱动模块RTL综合实现 总结 写在前面的话 之前在逛淘宝的时候偶然发现了Tang Nano 20K&#xff0c;十分感慨国产FPGA替代方案的进步之快&#xff1b;被…

自动生成测试用例_接口测试用例自动生成工具

前言 写用例之前&#xff0c;我们应该熟悉API的详细信息。建议使用抓包工具Charles或AnyProxy进行抓包。 har2case 我们先来了解一下另一个项目har2case 他的工作原理就是将当前主流的抓包工具和浏览器都支持将抓取得到的数据包导出为标准通用的 HAR 格式&#xff08;HTTP A…

【AI】安装ubuntu20.04教程(未完待续)

目录 1 制作ubuntu20.04系统盘1.1 下载ubuntu镜像1.2 使用ultraiso写入镜像 2 安装Ubuntu系统 1 制作ubuntu20.04系统盘 1.1 下载ubuntu镜像 在清华镜像站https://mirrors.tuna.tsinghua.edu.cn/下载ubuntu20.04镜像 路径为/ubuntu-releases/20.04/&#xff0c;下载ubuntu-20…

操作系统(16)----磁盘相关

目录 一.磁盘相关概念 1.磁盘 2.磁道 3.扇区 4.盘面、柱面 5.磁盘的分类 二.磁盘调度算法 1.一次磁盘读/写操作需要的时间 2.先来先服务算法(FCFS) 3.最短寻找时间优先(SSTF) 4.扫描算法(SCAN) 5.LOOK调度算法 6.循环扫描算法(C-SCAN) 7.C-LOOK调度算法 三.减少…

9.【CPP】List (迭代器的模拟实现||list迭代器失效||list的模拟实现)

介绍 list是可以在常数范围内在任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭代。list的底层是双向链表结构&#xff0c;双向链表中每个元素存储在互不相关的独立节点中&#xff0c;在节点中通过指针指向其前一个元素和后一个元素。list与forward_…

华为机考入门python3--(12)牛客12-字符串反转

分类&#xff1a;字符串 知识点&#xff1a; 字符串是否为空 if not my_str 字符串逆序 my_str[::-1] 题目来自【牛客】 def reverse_string(s): # 判断字符串是否为空或只包含空格 if not s.strip(): return "" # 使用Python的切片语法反转字符串 re…

Pytorch的可视化

1 使用 wandb进行可视化训练过程 本文章将从wandb的安装、wandb的使用、demo的演示进行讲解。 1.1 如何安装wandb&#xff1f; wandb的安装比较简单&#xff0c;在终端中执行如下的命令即可&#xff1a; pip install wandb在安装完成之后&#xff0c;我们需要&#xff0c;去…

matlab入门,在线编辑,无需安装matab

matlab相关教程做的很完善&#xff0c;除了B站看看教程&#xff0c;官方教程我觉得更加高效。跟着教程一步一步编辑&#xff0c;非常方便。 阅读 MATLAB 官方教程&#xff1a; MATLAB 官方教程提供了从基础到高级的教学内容&#xff0c;内容包括 MATLAB 的基本语法、数据处理…

Vue3高频知识点和写法

一 Vue插件 二 vue3项目创建 创建完成后npm install npm run dev 三 setup 一 响应式数据 setup函数是用来代替data和methods的写法的&#xff0c;在setup函数中声明的数据和函数&#xff0c;导出后可以在页面中使用。 但是暂时不是响应式数据&#xff0c;如果要响应式数据的…

C++笔记1:操纵符输入输出

C操纵符用来控制输出控制&#xff0c;一是输出的形式&#xff0c;二是控制补白的数量和位置。本文记录一下&#xff0c;在一些笔试的ACM模式可能有用。其中1-4节的部分是关于格式化输入输出操作&#xff0c;5-6节的部分是关于未格式化输入输出操作。 1. 控制布尔值的格式 一般…

C语言—基础数据类型(含进制转换)

进制转换不多&#xff0c;但我觉得适合小白(我爱夸自己嘿嘿) 练习 1. 确认基础类型所占用的内存空间(提示&#xff1a;使用sizeof 运算符)&#xff1a; 在这里我说一下&#xff0c;long 类型通常占用 4 字节。在 64 位系统上&#xff0c;long 类型通常也可为 8 字节。 格式…

LeetCode、208. 实现 Trie (前缀树)【中等,自定义数据结构】

文章目录 前言LeetCode、208. 实现 Trie (前缀树)【中等&#xff0c;自定义数据结构】题目链接与分类思路 资料获取 前言 博主介绍&#xff1a;✌目前全网粉丝2W&#xff0c;csdn博客专家、Java领域优质创作者&#xff0c;博客之星、阿里云平台优质作者、专注于Java后端技术领…

低资源学习与知识图谱:构建与应用

目录 前言1 低资源学习方法1.1 数据增强1.2 特征增强1.3 模型增强 2 低资源知识图谱构建与推理2.1 元关系学习2.2 对抗学习2.3 零样本关系抽取2.4 零样本学习与迁移学习2.5 零样本学习与辅助信息 3 基于知识图谱的低资源学习应用3.1 零样本图像分类3.2 知识增强的零样本学习3.3…

云原生介绍与容器的基本概念

云原生介绍 1、云原生的定义 云原生为用户指定了一条低心智负担的、敏捷的、能够以可扩展、可复制的方式最大化地利用云的能力、发挥云的价值的最佳路径。 2、云原生思想两个理论 第一个理论基础是&#xff1a;不可变基础设施。 第二个理论基础是&#xff1a;云应用编排理…

备战蓝桥杯---图论基础理论

图的存储&#xff1a; 1.邻接矩阵&#xff1a; 我们用map[i][j]表示i--->j的边权 2.用vector数组&#xff08;在搜索专题的游戏一题中应用过&#xff09; 3.用邻接表&#xff1a; 下面是用链表实现的基本功能的代码&#xff1a; #include<bits/stdc.h> using nam…

纪念一下 开始写博客两周以来第一次入围榜单

两周里 创作博客 第一次进入热榜 虽然只有几十名 但也算是一个突破 确实没想到自己随便做的笔记就入围了 感谢各位大佬的支持&#xff01;继续进步&#xff01;&#x1f973;&#x1f973;&#x1f973;

服务流控(Sentinel)

引入依赖 <!-- 必须的 --> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId> </dependency><!-- sentinel 核心库 --> <dependency><groupId>com.ali…