科研工具tips


工具用法总结

Online Essay

pytorch篇

  • torch.matmul:用于执行矩阵乘法。它支持多种维度的矩阵运算,包括两个2D矩阵的乘法,以及高维矩阵的乘法(批量矩阵乘法)

该行为取决于张量的维数

  • 如果两个张量都是一维,则返回点积(标量)。
1
2
3
4
>>> # vector x vector
>>> tensor1 = torch.randn(3)
>>> tensor2 = torch.randn(3)
>>> torch.matmul(tensor1, tensor2).size()

image.png

  • 如果两个参数都是二维的,则返回矩阵-矩阵乘积。
1
2
3
4
5
>>> # batched matrix x batched matrix
>>> tensor1 = torch.randn(10, 3, 4)
>>> tensor2 = torch.randn(10, 4, 5)
>>> torch.matmul(tensor1, tensor2).size()
torch.Size([10, 3, 5])
  • 如果第一个参数是一维,第二个参数是二维,则为了矩阵乘法的目的,在其维度前添加 1。矩阵相乘后,前面的维度将被删除。
  • 如果第一个参数是二维的,第二个参数是一维的,则返回矩阵向量乘积。
1
2
3
4
5
>>> # matrix x vector
>>> tensor1 = torch.randn(3, 4)
>>> tensor2 = torch.randn(4)
>>> torch.matmul(tensor1, tensor2).size()
torch.Size([3])

每一行分别与向量相乘

image.png

  • 如果两个参数至少为一维且至少一个参数为 N 维(其中 N > 2),则返回批量矩阵乘法
1
2
3
4
5
6
7
8
9
10
11
>>> # batched matrix x broadcasted vector
>>> tensor1 = torch.randn(10, 3, 4)
>>> tensor2 = torch.randn(4)
>>> torch.matmul(tensor1, tensor2).size()
torch.Size([10, 3])

>>> # batched matrix x broadcasted matrix
>>> tensor1 = torch.randn(10, 3, 4)
>>> tensor2 = torch.randn(4, 5)
>>> torch.matmul(tensor1, tensor2).size()
torch.Size([10, 3, 5])

Parameters

• in_features (int) – size of each input sample
• out_features (int) – size of each output sample
• bias (bool) – If set to False, the layer will not learn an additive bias. Default: True

  • in_features (int) – size of each input sample
  • out_features (int) – size of each output sample
  • bias (bool) – If set to False, the layer will not learn an additive bias. Default: True

image.png

1
2
3
4
5
>>> m = nn.Linear(20, 30)
>>> input = torch.randn(128, 20)
>>> output = m(input)
>>> print(output.size())
torch.Size([128, 30])

PyTorch 允许张量成为现有张量的视图。视图张量与其基本张量共享相同的基础数据。支持视图避免了显式数据复制,从而使我们能够进行快速且内存高效的重塑、切片和逐元素操作

例如,要获取现有张量 t 的视图,可以调用 t.view(…)

1
2
3
4
5
t = torch.rand(4, 4)
b = t.view(2, 8)
t.storage().data_ptr() == b.storage().data_ptr() # `t` and `b` share the same underlying data.
b[0][0] = 3.14
t[0][0]
  • tensor transpose 转置行和列
1
2
3
4
5
6
7
8
base = torch.tensor([[0, 1,4],[2, 3,5]])
base.is_contiguous()
t = base.transpose(0, 1)
print(t)

tensor([[0, 2],
[1, 3],
[4, 5]])

在PyTorch中,torch.randint(low, high, size)函数用于生成一个随机整数张量。这里的lowhigh分别表示生成随机数的下限和上限(上限是不包含的),而size参数指定了输出张量的形状。

当你使用torch.randint(2, 8, (3,))时,你实际上是在创建一个形状为(3,)的一维张量,其中包含从2到7(包括2,不包括8)的随机整数。这里的(3,)表示这个张量有3个元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
batch_size = 2
# 单词表大小
max_source = 8
# src_len = torch.randint(2, 5,(batch_size,))
# tgt_len = torch.randint(2, 5,(batch_size,))
src_len = torch.tensor([2,4]).to(torch.int32)
tgt_len = torch.tensor([4,3]).to(torch.int32)
src_seq = [torch.randint(1, max_source,(L,)) for L in src_len]
tgt_seq = [torch.randint(1, max_source,(L,)) for L in tgt_len]

print(src_seq)
print(tgt_seq)
====
[tensor([2, 2]), tensor([1, 6, 5, 3])]
[tensor([1, 6, 3, 4]), tensor([6, 1, 6])]

用于在指定的维度上插入一个大小为1的维度。

例如,如果你有一个一维张量 x = torch.tensor([1, 2, 3, 4]),使用 torch.unsqueeze(x, 0) 会在位置0(最前面)插入一个新的维度,结果张量的形状会变成 (1, 4)。如果你使用 torch.unsqueeze(x, 1),则会在位置1(最后面)插入一个新的维度,结果张量的形状会变成 (4, 1)

在实际应用中,dim 参数的值决定了新维度被插入的位置。例如:

  • dim=0 会在张量的最前面插入一个新维度。
  • dim=-1 会在张量的最后面插入一个新维度。
  • 如果你的输入张量是二维的,比如 (3, 4),使用 torch.unsqueeze(input, 0) 会得到一个形状为 (1, 3, 4) 的张量,而 torch.unsqueeze(input, -1) 会得到一个形状为 (3, 4, 1) 的张量
  • dim=n,即在第n为插入一个维度

Parameters

  • input (Tensor) – the input tensor.
  • dim (int) – the index at which to insert the singleton dimension
1
2
3
4
5
6
7
8
x = torch.tensor([1, 2, 3, 4])
torch.unsqueeze(x, 0)
tensor([[ 1, 2, 3, 4]])
torch.unsqueeze(x, 1)
tensor([[ 1],
[ 2],
[ 3],
[ 4]])

对input和 mat2 中存储的矩阵执行批量矩阵-矩阵乘积
input 和 mat2 必须是 3-D 张量,每个张量包含相同数量的矩阵。

if input is a (b×n×m) tensor, mat2 is a (b×m×p) tensor, out will be a (b×n×p)tensor.

1
2
3
4
5
input = torch.randn(10, 3, 4)
mat2 = torch.randn(10, 4, 5)
res = torch.bmm(input, mat2)
res.size()
》》torch.Size([10, 3, 5])

numpy

  • np.array:用于创建一个数组。这个数组可以是一维的,也可以是多维的,并且可以包含任何数据类型,如整数、浮点数、字符串等。数组中的所有元素必须是相同的数据类型。
1
np.array([1, 2, 3, 4, 5])
  • np.power(x, y): 这里x可以是数组

$$
np.power(x, y) = x^y
$$

  • np.sin() sp.cos():
1
2
np.sin(np.pi/2.)
》》 1.0
  • np.zero(x):补充x位的0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
a = np.arange(6).reshape((3, 2))
a
[0 1 2 3 4 5] ->
array([[0, 1],
[2, 3],
[4, 5]])


np.reshape(a, (2, 3)) # C-like index ordering
array([[0, 1, 2],
[3, 4, 5]])
np.reshape(np.ravel(a), (2, 3)) # equivalent to C ravel then C reshape
array([[0, 1, 2],
[3, 4, 5]])
np.reshape(a, (2, 3), order='F') # Fortran-like index ordering
array([[0, 4, 3],
[2, 1, 5]])
np.reshape(np.ravel(a, order='F'), (2, 3), order='F')
array([[0, 4, 3],
[2, 1, 5]])

数组的上三角形。
返回一个数组的副本,其中第 k 个对角线下方的元素已为零。对于 ndim 超过 2 的数组,triu 将应用于最后两个轴

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import numpy as np
# k = 0,对角线以下全为0, k > 1向右上平移,k < 0向坐下平移
np.triu([[1,2,3],[4,5,6],[7,8,9],[10,11,12]],1)

array([[0, 2, 3],
[0, 0, 6],
[0, 0, 0],
[0, 0, 0]])

np.triu(np.arange(3*4*5).reshape(3, 4, 5))
array([[[ 0, 1, 2, 3, 4],
[ 0, 6, 7, 8, 9],
[ 0, 0, 12, 13, 14],
[ 0, 0, 0, 18, 19]],
[[20, 21, 22, 23, 24],
[ 0, 26, 27, 28, 29],
[ 0, 0, 32, 33, 34],
[ 0, 0, 0, 38, 39]],
[[40, 41, 42, 43, 44],
[ 0, 46, 47, 48, 49],
[ 0, 0, 52, 53, 54],
[ 0, 0, 0, 58, 59]]])

同理,
numpy.tril:数组的下三角形。

Latex篇

论文轮廓

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
\documentclass[twocolumn]{article}
\documentclass{article}
\usepackage{amsmath}
\usepackage{graphicx}
\usepackage{hyperref}
\usepackage{geometry}
\geometry{a4paper, margin=1in}

\title{your paper's name}
\author{jingxiao yang}
\date{\today}

\begin{document}

\maketitle

\begin{abstract}
The abstract is written here
\end{abstract}

\section{Introduction}
% Introduction content goes here...

\section{Related Work}
% Related Work content goes here...

\section{Methodology}
% Methodology content goes here...

\section{Experiments and Results}
% Experiments and Results content goes here...

\section{Conclusion}
% Conclusion content goes here...

\bibliographystyle{plain}
\bibliography{references}

\end{document}

图片插入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
\usepackage{graphicx}  % 在导言区引入graphicx包
\begin{figure}[h]
\centering
\includegraphics[width=\textwidth]{图片路径}
\caption{图片说明}
\label{fig:example}
\end{figure}

\begin{figure}[h]
\centering
\includegraphics[width=0.8\textwidth]{self-examination.png}
\caption{The potentially harmful response generated from an LLM is inserted into a predefined prompt format and provided to a harm filter. The filter classifies the response as either “harmful” or “harmless.”}
\label{fig:self-examination}
\end{figure}

文献引用

1
2
3
4
5
6
latex
复制代码
\begin{thebibliography}{99}
\bibitem{引用标记} 作者. 文章标题. 期刊名, 年份.
\end{thebibliography}

• 使用 \cite{引用标记} 在正文中引用参考文献。

1
2
\cite{ref1}
\bibitem{ref1} Reference 1 Title. Journal Name, Year.

数学公式

Transformers

参考

pipelines

官方文档

Transformers 库中最基本的对象是pipeline()函数。它将模型与其必要的预处理和后处理步骤连接起来,使我们能够直接输入任何文本并获得答案。当第一次运行的时候,它会下载预训练模型和分词器(tokenizer)并且缓存下来

1
2
3
4
5
6
7
from transformers import pipeline

classifier = pipeline("sentiment-analysis") # 情感分析
classifier("I've been waiting for a HuggingFace course my whole life.")

# 输出
# [{'label': 'POSITIVE', 'score': 0.9598047137260437}]
1
2
3
4
5
6
7
8
classifier(
["I've been waiting for a HuggingFace course my whole life.", "I hate this so much!"]
)

# 输出
'''
[{'label': 'POSITIVE', 'score': 0.9598047137260437},
{'label': 'NEGATIVE', 'score': 0.9994558095932007}]

pipeline()在接收文本后,通常有三步:Tokenizer、Model、Post-Processing

1)Tokenizer

与其他神经网络一样,Transformer 模型不能直接处理原始文本,故使用分词器进行预处理。使用AutoTokenizer类及其from_pretrained()方法

1
2
3
4
from transformers import AutoTokenizer

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)

若要指定我们想要返回的张量类型(PyTorch、TensorFlow 或普通 NumPy),我们使用return_tensors参数

1
2
3
4
5
6
raw_inputs = [
"I've been waiting for a HuggingFace course my whole life.",
"I hate this so much!",
]
inputs = tokenizer(raw_inputs, padding=True, truncation=True, return_tensors="pt")
print(inputs)

PyTorch 张量的结果:

输出本身是一个包含两个键的字典,input_idsattention_mask

1
2
3
4
5
6
7
8
9
10
{
'input_ids': tensor([
[ 101, 1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012, 102],
[ 101, 1045, 5223, 2023, 2061, 2172, 999, 102, 0, 0, 0, 0, 0, 0, 0, 0]
]),
'attention_mask': tensor([
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
])
}

2)Model

Transformers 提供了一个AutoModel类,它也有一个from_pretrained()方法:

1
2
3
4
from transformers import AutoModel

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
model = AutoModel.from_pretrained(checkpoint)

如果我们将预处理过的输入提供给我们的模型,我们可以看到:

1
2
3
4
5
outputs = model(**inputs)
print(outputs.last_hidden_state.shape)

# 输出
# torch.Size([2, 16, 768])

Transformers 中有许多不同的架构可用,不同的模型架构被设计用于特定的自然语言处理(NLP)任务。每种架构在基础模型的基础上添加了不同的“头部”(head),以适应特定的任务需求,清单:

*Model (retrieve the hidden states)
*ForCausalLM
*ForMaskedLM
*ForMultipleChoice
*ForQuestionAnswering
*ForSequenceClassification
*ForTokenClassification
and others

Model

不同的模型架构被设计用于特定的自然语言处理(NLP)任务。每种架构在基础模型的基础上添加了不同的“头部”(head),以适应特定的任务需求

用途:

  • 特征提取:用作其他任务的特征提取器,获取输入文本的上下文表示。
  • 自定义任务:在基础模型上添加自定义的输出头部,以适应特定需求。
  • 研究目的:分析模型内部的表示或进行模型蒸馏等研究工作
1
2
3
4
5
6
7
8
9
from transformers import BertModel, BertTokenizer

tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertModel.from_pretrained('bert-base-uncased')

inputs = tokenizer("Hello, world!", return_tensors="pt")
outputs = model(**inputs)
hidden_states = outputs.last_hidden_state

ForCausalLM(因果语言建模)

  • ForCausalLM 类(例如 GPT2LMHeadModel)专为因果语言建模任务设计。因果语言模型通过仅利用序列中前面的词(左侧上下文)来预测下一个词,实现文本生成。

用途:

  • 文本生成:自动补全、续写、生成创意文本。
  • 对话系统:构建聊天机器人,生成对话回复。
  • 内容创作:辅助写作、代码生成等
1
2
3
4
5
6
7
8
9
from transformers import GPT2LMHeadModel, GPT2Tokenizer

tokenizer = GPT2Tokenizer.from_pretrained('gpt2')
model = GPT2LMHeadModel.from_pretrained('gpt2')

inputs = tokenizer("Once upon a time", return_tensors="pt")
outputs = model.generate(**inputs, max_length=50)
generated_text = tokenizer.decode(outputs[0], skip_special_tokens=True)

ForMaskedLM(掩码语言建模)

  • *ForMaskedLM 类(例如 BertForMaskedLM)专为掩码语言建模任务设计。这类模型通过预测序列中被掩盖的词来理解上下文,利用双向(前后)上下文信息。

用途:

  • 填充缺失词:在句子中填补被掩盖的词语。
  • 预训练任务:用于预训练模型,如 BERT。
  • 文本理解:增强模型对上下文的理解能力。
1
2
3
4
5
6
7
8
9
10
11
12
from transformers import BertForMaskedLM, BertTokenizer

tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertForMaskedLM.from_pretrained('bert-base-uncased')

inputs = tokenizer("The capital of France is [MASK].", return_tensors="pt")
outputs = model(**inputs)
logits = outputs.logits
masked_index = (inputs.input_ids == tokenizer.mask_token_id).nonzero(as_tuple=True)[1]
predicted_token_id = logits[0, masked_index].argmax(dim=-1)
predicted_token = tokenizer.decode(predicted_token_id)

ForMultipleChoice

  • ForMultipleChoice 类(例如 BertForMultipleChoice)用于多选题任务。该模型能够处理多个选项,并评估每个选项与给定上下文或问题的匹配程度,选择最合适的答案。

用途:

  • 多选问答:选择最佳答案,如标准化考试问题。
  • 推荐系统:从多个候选项中选择最适合的选项。
  • 决策支持:在多种可能的行动方案中进行选择。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from transformers import BertForMultipleChoice, BertTokenizer
import torch

tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertForMultipleChoice.from_pretrained('bert-base-uncased')

question = "What is the capital of France?"
options = ["Berlin", "London", "Paris", "Rome"]

encoding = tokenizer([question]*len(options), options, return_tensors='pt', padding=True)
outputs = model(**encoding)
logits = outputs.logits
answer = torch.argmax(logits).item()
selected_option = options[answer]

ForQuestionAnswering(问答)

描述:

  • *ForQuestionAnswering 类(例如 BertForQuestionAnswering)专为问答任务设计。模型通过给定上下文和问题,预测答案在上下文中的起始和结束位置。

用途:

  • 信息检索:从文档中提取精确答案。
  • 对话系统:在对话中提供具体答案。
  • 知识问答:基于知识库或文本回答问题。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from transformers import BertForQuestionAnswering, BertTokenizer
import torch

tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertForQuestionAnswering.from_pretrained('bert-base-uncased')

question = "What is the capital of France?"
context = "France's capital is Paris, which is known for its culture and history."

inputs = tokenizer(question, context, return_tensors='pt')
outputs = model(**inputs)
start_scores = outputs.start_logits
end_scores = outputs.end_logits

start_index = torch.argmax(start_scores)
end_index = torch.argmax(end_scores) + 1
answer = tokenizer.convert_tokens_to_string(tokenizer.convert_ids_to_tokens(inputs.input_ids[0][start_index:end_index]))

ForSequenceClassification(序列分类)

描述:

  • *ForSequenceClassification 类(例如 BertForSequenceClassification)用于对整个输入序列进行分类。模型在基础模型的顶部添加了一个分类头部,以输出类别的概率分布。

用途:

  • 情感分析:判断文本的情感倾向(如积极、消极、中性)。
  • 主题分类:将文本归类到不同的主题或类别中。
  • 垃圾邮件检测:识别并分类垃圾邮件。
  • 语言检测:确定文本的语言类别。

示例:

1
2
3
4
5
6
7
8
9
10
11
from transformers import BertForSequenceClassification, BertTokenizer
import torch

tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertForSequenceClassification.from_pretrained('bert-base-uncased', num_labels=2)

sentence = "I love using Hugging Face transformers!"
inputs = tokenizer(sentence, return_tensors='pt')
outputs = model(**inputs)
logits = outputs.logits
predicted_class = torch.argmax(logits).item()

ForTokenClassification (标记分类)

描述:

  • ForTokenClassification 类(例如 BertForTokenClassification)专为标记级分类任务设计。模型在每个输入标记(token)的表示上添加了分类头部,以对每个标记进行分类。

用途:

  • 命名实体识别(NER):识别文本中的实体,如人名、地名、组织机构等。
  • 词性标注(POS):为每个词分配词性标签,如名词、动词等。
  • 文本分块:将文本分成不同的块或片段,进行标签分类。
  • 语义角色标注:识别句子中各个成分的语义角色。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from transformers import BertForTokenClassification, BertTokenizer
import torch

tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertForTokenClassification.from_pretrained('bert-base-uncased', num_labels=9)

sentence = "Hugging Face is based in New York City."
inputs = tokenizer(sentence, return_tensors='pt')
outputs = model(**inputs)
logits = outputs.logits
predicted_labels = torch.argmax(logits, dim=2)

tokens = tokenizer.convert_ids_to_tokens(inputs.input_ids[0])
labels = predicted_labels[0].tolist()

for token, label in zip(tokens, labels):
print(f"{token}: {label}")

3)Post-Processing

模型最后一层输出的原始**非标准化分数**。要转换为概率,它们需要经过一个SoftMax层(所有 Transformers 模型都输出 logits,因为用于训练的损耗函数一般会将最后的激活函数(如SoftMax)与实际损耗函数(如交叉熵)融合 。

1
2
3
4
import torch

predictions = torch.nn.functional.softmax(outputs.logits, dim=-1)
print(predictions)

model

1)创建Transformer

1
2
3
4
5
6
7
from transformers import BertConfig, BertModel

# Building the config
config = BertConfig()

# Building the model from the config
model = BertModel(config)

2)不同的加载方式

1
2
3
from transformers import BertModel

model = BertModel.from_pretrained("bert-base-cased")

3)保存模型

1
model.save_pretrained("directory_on_my_computer")

4)使用Transformer model

1
2
3
4
5
6
7
8
9
10
sequences = ["Hello!", "Cool.", "Nice!"]
encoded_sequences = [
[101, 7592, 999, 102],
[101, 4658, 1012, 102],
[101, 3835, 999, 102],
]

import torch

model_inputs = torch.tensor(encoded_sequences)

model.generate()总结

第一次做实验,对于各个参数的不熟悉导致我花了导量时间进行代码调试,现在进行总结,帮助我理解。

参数

  • top_p 核采样

    • 核采样是一种基于概率分布的采样方法,用于从语言模型生成的词汇分布中选择下一个词。它的核心思想是选择概率累积达到阈值 p 的最小词汇集合(称为“核”),然后从这个集合中进行采样
    • 工作过程
      • 排序:首先,将所有候选词按概率从高到低排序。
      • 累积概率:计算从最高概率开始,逐步累加词汇的概率。
      • 选择核:确定最小的词汇集合,使其累积概率至少为 p。这个集合就是“核”。
      • 采样:从这个“核”中按概率分布随机选择下一个词汇。
    • 较低的 p(如0.6)会限制模型只从高概率词汇中选择,生成内容更确定但可能缺乏多样性。较高的 p(如0.9)则允许更多低概率词汇参与采样,增加生成内容的多样性
  • max_length

    • 控制生成文本的最大长度。包括输入token的数量在内
  • min_length

    • 设置生成文本的最小长度,确保生成的文本不会太短
  • do_sample

    • 是否在生成文本时使用采样策略。如果设置为False,则使用贪心策略进行解码(贪心算法可能导致局部最优)
  • temperature

    • 用于调整生成过程中的随机性程度。数值越大,生成的文本越随机
  • repetition_penalty

    • 用来防止文本重复的参数,使得模型在选择已经出现的词时变得更加谨慎。为了避免生成的文本过于重复,通过调节这个参数,可以降低重复词汇的选择概率,提高文本的多样性和可读性
  • pad_token_id

    • 用于填充的token ID。在文本生成中,如果生成的文本短于max_length,这个ID将被用来填充生成文本
  • eos_token_id

    • 定义结束序列的token ID。当模型生成了这个ID对应的token时,将停止生成进一步的token。
  • num_beams

    • 使用束搜索(Beam Search)策略时,束的大小。提高这个值可以提高文本的质量,但也会增加计算量
  • use_cache

    • 是否使用模型的past key/values来加速生成。对于某些模型,这可以显著提高生成的速度。

防止EOS过早出现

  • 调整max_lengthmin_length

  • 使用eos_token_idpad_token_id参数

    • 如果知道EOS标记的token ID,可以尝试在生成时不包含该EOS标记的ID,或者通过使用pad_token_id替换掉eos_token_id,让模型知道还不应该结束。
  • 调整length_penalty

    • length_penalty参数用于调节生成文本的长度偏好。数值大于1会鼓励模型生成更长的序列,可能有助于减少EOS标记过早出现的情况
  • 清晰的任务指示(Prompt Engineering)

  • 对EOS进行惩罚

    • 使用logit_bias参数调整EOS的生成概率。logit_bias参数允许我们为特定的token IDs设置一个偏置值,从而影响它们被生成的概率。通过给EOS token设置一个负的偏置值,可以降低它被早期选择的可能性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
from transformers import AutoTokenizer, AutoModelForCausalLM

tokenizer = AutoTokenizer.from_pretrained("gpt2")
model = AutoModelForCausalLM.from_pretrained("gpt2")

input_ids = tokenizer("Example input text", return_tensors="pt").input_ids
eos_token_id = tokenizer.eos_token_id

# 创建 logit_bias 字典,给EOS token ID一个负偏置
logit_bias = {eos_token_id: -100}

# 生成文本,应用logit_bias
generated_ids = model.generate(input_ids, logit_bias=logit_bias, max_length=50)
print(tokenizer.decode(generated_ids[0], skip_special_tokens=True))

常用算法

image-20241101192612778

  • greedy decoding:当 num_beams=1 而且 do_sample=False 时,调用 greedy_search()方法,每个step生成条件概率最高的词,因此生成单条文本。

贪婪搜索,每个时间步 t tt 都选概率最高的那个词

$$w_t=argmax_wP(w|w_{1:t-1})$$

  • multinomial sampling:当 num_beams=1 且 do_sample=True 时,调用 sample() 方法,对词表做一个采样,而不是选条件概率最高的词,增加多样性。
  • beam-search decoding:当 num_beams>1 且 do_sample=False 时,调用 beam_search() 方法,做一个 num_beams 的柱搜索,每次都是贪婪选择top N个柱。

beam search每个时间步选择最可能的 Top - num_beams 个词,解决了贪婪搜索擦肩而过的风险。如图例子,num_beams=2,第一步选了概率最高的序列 the nice(0.5) 和 the dog(0.4),第二步选了概率最高的序列 the dog has(0.4✖️0.9=0.36)和 the nice woman(0.5✖️0.4=0.20)。

如果生成长度是提前可预知,比如摘要、翻译,这种用beam search好;但开放式生成,比如对话,故事生成等,输出长度变化比较大,就不太适合用beam search了。
beam search容易重复生成单词。由于通过大量实验才能达到“禁止生成重复n-gram” 和 “允许周期性生成重复n-gram” 的平衡,所以在开放式生成任务上不太好用这种惩罚来控制重复。
人类往往说话时不是总选择高概率的词作为下个词,而是经常猝不及防,出其不意,如图对比。所以beam search还是有很大问题的。

  • beam-search multinomial sampling:当 num_beams>1 且 do_sample=True 时,调用 beam_sample() 方法,相当于每次不再是贪婪选择top N个柱,而是加了一些采样。
  • diverse beam-search decoding:当 num_beams>1 且 num_beam_groups>1 时,调用 group_beam_search() 方法。
  • constrained beam-search decoding:当 constraints!=None 或者 force_words_ids!=None,实现可控文本生成

补充

切片操作

L[-1]取倒数第一个元素,那么它同样支持倒数切片

  • L[-2:] 取最后两个元素
1
2
3
4
5
6
7
8
9
10
L = list(range(100))
L[:10] #取出前十个数
L[-10:] #取出后十个数
#前10个数,每两个取一个
L[:10:2]
#所有数,每5个取一个
L[::5]
#复制
L[:]

[1:, 0::2]

  1. **1:**:
    • 这部分表示选择索引从1到数组末尾的所有行。在Python中,索引是从0开始的,所以1:意味着从第二行开始,一直到数组的最后一行。
  2. **0::2**:
    • 这部分表示选择列。这里的0表示从第一列开始,2表示步长,即每隔一列选择一列。因此,0::2意味着选择所有行的第一列、第三列、第五列等,也就是所有偶数列(如果从1开始计数的话)。

扩充 [] 中的用法

切片操作可以通过不同的参数来灵活选择数组的一部分。以下是一些常见的用法:

  1. **start:**:
    • 选择从start索引到数组末尾的所有元素。
    • 例如,3:会选择从索引3开始的所有元素。
  2. **:start**:
    • 选择从第一个元素到start-1索引的所有元素。
    • 例如,:3会选择从索引0到2的元素。
  3. **start:stop**:
    • 选择从start索引到stop-1索引的所有元素。
    • 例如,1:4会选择从索引1到3的元素。
  4. **start:stop:step**:
    • 选择从start索引到stop-1索引之间,每隔step个元素选择一个元素。
    • 例如,1:5:2会选择索引1、3的元素。
  5. **::step**:
    • 选择所有元素,每隔step个元素选择一个元素。
    • 例如,::2会选择索引0、2、4等的元素。
  6. **start::step**:
    • 选择从start索引开始的所有元素,每隔step个元素选择一个元素。
    • 例如,2::2会选择索引2、4、6等的元素。
  7. **:stop:step**:
    • 选择从第一个元素到stop-1索引之间,每隔step个元素选择一个元素。
    • 例如,:4:2会选择索引0、2的元素。

具体例子

假设有一个数组如下:

1
2
3
4
5
6
plaintext
[[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]]

  1. **1:, 0::2**:
    • 结果:[[ 4, 6], [ 8, 10], [12, 14]]
  2. **:3, :2**:
    • 结果:[[ 0, 1], [ 4, 5], [ 8, 9]]
  3. **:, 1:3**:
    • 结果:[[ 1, 2], [ 5, 6], [ 9, 10], [13, 14]]
  4. **2:, ::2**:
    • 结果:[[ 8, 9, 10, 11], [12, 13, 14, 15]]
  5. **:, 0:3:2**:
    • 结果:[[ 0, 2], [ 4, 6], [ 8, 10], [12, 14]]

通过这些切片操作,你可以灵活地选择和操作数组的特定部分。

分布式训练DP和DDP

blog

使用 DDP 进行多卡并行加速模型的重点:

  • init_process_group 函数管理进程组
  • 在创建 Dataloader 的过程中,需要使用 DistributedSampler 采样器
  • 正反向传播之前需要将数据以及模型移动到对应 GPU,通过参数 rank 进行索引,还要将模型使用 DistributedDataParallel 进行包装
  • 在每个 epoch 开始之前,需要使用 train_sampler.set_epoch(epoch)为 train_sampler 指定 epoch,这样做可以使每个 epoch 划分给不同进程的 minibatch 不同,从而在整个训练过程中,不同的进程有机会接触到更多的训练数据
  • 使用启动器进行启动。不同启动器对应不同的代码。torch.distributed.launch 通过命令行的方法执行,torch.multiprocessing.spawn 则可以直接运行程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import os
import torch
import torch.distributed as dist
import torch.nn as nn
import torch.optim as optim
import torch.multiprocessing as mp

from torch.nn.parallel import DistributedDataParallel as DDP


def setup(rank, world_size):
os.environ['MASTER_ADDR'] = 'localhost'
os.environ['MASTER_PORT'] = '12355'

# initialize the process group
dist.init_process_group("nccl", rank=rank, world_size=world_size)

def run(demo_fn, world_size):
setup(rank, world_size)

torch.manual_seed(18)
torch.cuda.manual_seed_all(18)
torch.backends.cudnn.deterministic = True
torch.cuda.set_device(rank) # 这里设置 device ,后面可以直接使用 data.cuda(),否则需要指定 rank


train_dataset = ...
train_sampler = torch.utils.data.distributed.DistributedSampler(train_dataset)
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=..., sampler=train_sampler)

model = ...
model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[rank])

optimizer = optim.SGD(model.parameters())

for epoch in range(100):
train_sampler.set_epoch(epoch)
for batch_idx, (data, target) in enumerate(train_loader):
data = data.cuda()
target = target.cuda()
...
output = model(images)
loss = criterion(output, target)
...
optimizer.zero_grad()
loss.backward()
optimizer.step()



if __name__ == "__main__":
n_gpus = torch.cuda.device_count()
assert n_gpus >= 2, f"Requires at least 2 GPUs to run, but got {n_gpus}"
world_size = n_gpus

mp.spawn(run,
args=(world_size,),
nprocs=world_size,
join=True)

useful blogs

  1. 阮一峰的日志

包括技术分享、时事新闻等

  1. v_july_v

专精ai

  1. 稀土掘金

前司论坛,比较活跃并且还是有高质量文章

  1. coolshell

陈浩的个人网站,偏研发相关


文章作者: jingxiaoyang
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 jingxiaoyang !
评论
  目录