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

news2024/11/24 1:38:55

本课程主要介绍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, Embedding

embedding_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, Embedding

embedding_dim = 8

model = 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 optimizers

epochs = 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, Dense

vocabulary = 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 optimizers

epochs = 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, Dense

vocabulary = 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, Flatten

vocabulary = 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, Dense

vocabulary = 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, Bidirectional

vocabulary = 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.coloradmin.cn/o/1445723.html

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

相关文章

【DDD】学习笔记-统一语言与领域分析模型

无论你采用什么样的软件开发过程&#xff0c;对于一个复杂的软件系统&#xff0c;都必然需要通过分析阶段对问题域展开分析&#xff0c;如此才能有的放矢地针对该软件系统的需求寻找设计上的解决方案。在领域驱动设计中&#xff0c;分析阶段完全围绕着“领域”为中心展开&#…

RabbitMQ(保姆级教程)

RabbitMQ学习 基础 1. 同步通信和异步通信 同步调用 下一步动作必须依赖上一步 异步调用 通知到位就行&#xff0c;不对消费者做强制要求&#xff0c;只要求最终一致性就行 2. MQ技术选项 消息先进先出&#xff0c;RabbitMQ默认有序 Erlang 是面向并发&#xff0c…

STM32 + ESP8266,连接阿里云 上报/订阅数据

&#xff08;文章正在编辑中&#xff0c;一点点地截图操作过程&#xff0c;估计要拖拉两三天&#xff09; 一、烧录MQTT固件 ESP8266出厂时&#xff0c;默认是AT固件。连接阿里云&#xff0c;需要使用MQTT固件。 1、独立EPS8266模块的烧录方法 2、魔女开发板&#xff0c;板载…

备战蓝桥杯---组合数学基础1

让我们来几道高中的组合题吧&#xff1a; 1.我们一定有n个向下&#xff0c;为 2.我们挑最大的两个&#xff0c;条件是他们奇偶性相同&#xff0c;为2*A10,2; 3.用捆绑法即可。 4.我们用隔板法&#xff0c;为 5.问题等价于23个相同的球放到3个盒子里&#xff0c;每个盒子至少…

【北邮鲁鹏老师计算机视觉课程笔记】04 fitting 拟合

【北邮鲁鹏老师计算机视觉课程笔记】04 fitting 拟合 1 拟合的任务 如何从边缘找出真正的线&#xff1f; 存在问题 ①噪声 ②外点、离群点 ③缺失数据 2 最小二乘 存在的问题 3 全最小二乘 度量的是点到直线的距离而不是点在y方向到直线的距离 提示&#xff1a;点到直线的…

ChatGPT偷懒、变慢的罪魁祸首竟然是它?!系统提示词塞满垃圾!

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;所以创建了“AI信息Gap”这个公众号&#xff0c;专注于分享AI全维度知识…

【Java程序设计】【C00263】基于Springboot的分布式架构网上商城(有论文)

基于Springboot的分布式架构网上商城&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的网上商城 本系统分为管理员功能模块以及系统功能模块。 系统功能模块&#xff1a;在系统首页可以查看首首页、商品信息、购物…

【Git】移除Git中的文件

有的时候需要移除或者更新 Git 中的文件&#xff0c;我们无法直接在远程仓库中移除&#xff0c;移除或者更新操作需要在本地端实现。 1、移除被跟踪文件 当某个文件被添加到暂存区或者本地仓库&#xff0c;此时会被标记为“跟踪状态”&#xff0c;此时 Git 就会代为管理这个文…

肿瘤微环境异质性对治疗反应的影响(综述)

Influence of tumour micro-environment heterogeneity on therapeutic response | Nature 肿瘤的形成涉及肿瘤细胞与细胞外基质、肿瘤血管和免疫细胞的共同进化。肿瘤的成功生长和最终转移并不完全取决于肿瘤细胞的基因改变&#xff0c;还取决于这种突变在特定环境中带来的适…

通过QT制作一个模仿微信主界面的界面(不要求实现具体通信功能)

main.cpp #include "widget.h" #include "second.h"#include <QApplication>int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();//实例化第二个界面Second s;QObject::connect(&w, &Widget::my_jump, &…

STM32F1 引脚重映射功能

STM32 端口引脚重映射 文章目录 STM32 端口引脚重映射前言1、查阅芯片数据手册1.1 串口引脚重映射描述 2、代码部分2.1 核心代码部分 3、实验现象4、总结 前言 在写程序时遇到想要的端口功能&#xff0c;而这个引脚又被其它的功能占用了无法删除掉或直接使用&#xff0c;这种情…

Netty应用(三) 之 NIO开发使用 网络编程 多路复用

目录 重要&#xff1a;logback日志的引入以及整合步骤 5.NIO的开发使用 5.1 文件操作 5.1.1 读取文件内容 5.1.2 写入文件内容 5.1.3 文件的复制 5.2 网络编程 5.2.1 accept&#xff0c;read阻塞的NIO编程 5.2.2 把accept&#xff0c;read设置成非阻塞的NIO编程 5.2.3…

基础IO[一]

文件文件内容属性 文件在硬盘上放着&#xff0c;我们的流程->写代码->编译->运行->访问文件。那么本质上是谁在访问&#xff1f; 是进程在访问。进程访问文件是需要通过接口来访问。 文件在磁盘上放着&#xff0c;要向硬件写入文件&#xff0c;谁有权限呢?必须…

腾讯云4核8G服务器多少钱?2024精准报价

腾讯云4核8G服务器S5和轻量应用服务器优惠价格表&#xff0c;轻量应用服务器和CVM云服务器均有活动&#xff0c;云服务器CVM标准型S5实例4核8G配置价格15个月1437.3元&#xff0c;5年6490.44元&#xff0c;标准型SA2服务器1444.8元一年&#xff0c;轻量应用服务器4核8G12M带宽一…

MySQL-----DCL基础操作

▶ DCL简介 DCL英文全称是Data ControlLanguage(数据控制语言)&#xff0c;用来管理数据库用户、控制数据库的访问权限。 DCL--管理用户 ▶ 查询用户 use mysql; select * from user; ▶ 创建用户 ▶ 语法 create user 用户名主机名 identified by 密码 设置为在任意主机上访问…

在虚拟机上完成Centos安装

Linux学习和使用 前言如何安装Centos初始化操作 使用VMware备份操作系统快照克隆 内容总结参考链接 本人介绍:2023年全国大学生数学建模竞赛国家二等奖,2022年蓝桥杯省二等奖,这里是一个和你一起不断努力,不断前进的程序猿一枚 前言 简单介绍一下本片文章将会讲到的内容:本章节…

通过容器化释放云的力量

NCSC (英国国家网络安全中心) 经常被问到的一个问题是是否在云中使用容器。这是一个简单的问题&#xff0c;但答案非常微妙&#xff0c;因为容器化的使用方式有很多种&#xff0c;其中一些方法比其他方法效果更好。 今天&#xff0c;我们发布了有关使用容器化的安全指南&#…

寒假作业

手写盗版微信登入界面 #include "mainwindow.h" #include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow) {ui->setupUi(this);this->resize(421,575);this->setFixedSize(421,575);th…

线性表的链式存储结构

顺序存储结构不足的解决方法 顺序存储结构有缺点&#xff0c;最大的缺点是插入删除时需要移动大量元素&#xff0c;会导致时间效率下降。 线性表链式存储结构的定义 线性表的链式存储结构的特点是用一组任意的存储单元存储线性表的数据元素&#xff0c;这些存储单元可以是连…

Impala-架构与设计

架构与设计 一、背景和起源二、框架概述1.设计特点2.框架优点3.框架限制 三、架构图1.Impala Daemon2.Statestore3.Catalog 四、Impala查询流程1.发起查询2.生成执行计划3.分配任务4.交换中间数据5.汇集结果6.返回结果 总结参考链接 一、背景和起源 现有的大数据查询分析工具H…