NetLLM: Adapting Large Language Models for Networking.

NetLLM: Adapting Large Language Models for Networking.

Glossary

Rule Engineering: 在网络系统中创建和实现基于规则的算法的过程,这些规则是预先设定的,在基于规则的系统中,每一个操作和决策都根据静态的、预先定义好的规则来执行。

Model Engineering: 设计、实施和训练机器学习模型,不依赖于硬编码的规则,而是通过数据驱动的方式学习如何从输入数据到输出决策的映射。主要挑战是设计出能够在复杂网络环境中有效工作的模型架构,并且这些模型需要在不同的网络任务中重新训练和调整。

Adaptive Bitrate Streaming (ABR) Model: 用于视频传输中的一种技术,根据用户的网络条件动态调整视频的比特率。此类模型需要能够快速响应网络环境的变化,并做出准确的比特率调整决策,以保证视频流的平滑播放和高质量。

Resource-intensive: 某些任务或过程在执行时需要消耗大量计算资源。

Token: 指的是文本中的一个词或标点符号,它是文本分词(Tokenization)的结果,是自然语言处理中用于文本分析和处理的基本单元。

Efficient fine-tuning technology: 使用优化的策略来调整预训练模型(如大型语言模型)的参数,在保持预训练模型泛化能力的同时,减少所需的资源(如计算时间和内存)和提高模型调整的效率。

hallucination: 模型生成的输出中包含的错误或与现实不符的信息。

(Experience) Trajector: 指在与环境交互过程中收集的一系列事件,轨迹反映了从开始到结束的一系列决策和结果,通常用于训练或微调强化学习模型。

CDF: CDF 表示随机变量 X小于或等于某个特定值 x 的概率

Notes

INTRODUCTION

The Main Roadmap so far

基于学习的算法存在两个关键限制:

  • 高模型工程成本
  • 低泛化能力
New Opportunities and Challenges

适应网络的LLM所面临的挑战:

  • 大的输入模态差异:LLM的输入为纯文本,但网络任务中输入的信息格式多样。
  • 回答生成的低效率:为网络任务生成的答案可能看似正确但实际上无效、LLM可能无法快速生成答案以响应网络系统的变化
  • 高适应成本:LLM的参数量很大,微调的成本代价太高
Design and Contributions

NetLLM包括以下三个核心设计:

  • Multimodal encoder:处理非文本信息
  • Networking head:输出非文本内容
  • Data-driven low-rank networking adaptation (DDLRNA):数据驱动的方式训练LLM,降低微调成本

NetLLM特性:

  • 兼容性
  • 可靠性
  • 效率

BACKGROUND

Learning-Based Networking Algorithms

基于学习的算法设计和训练DNNs,通常采用SL和RL来促进学习过程,有效地学习解决网络任务,其限制为:

  • 高开销:需要为不同的网络任务手动设计专门的DNN模型
  • 泛化弱:DNN在未见数据分布/环境中可能表现不佳
Large Language Models

在这里插入图片描述

MOTIVATION

任务DNN输入DNN输出目标学习范式
视口预测 (VP)时间序列:历史视点;图像:视频内容信息未来视点最小化预测视点与实际视点之间的误差监督学习 (SL)
自适应比特率流 (ABR)时间序列:历史吞吐量,延迟;序列:不同比特率的块大小;标量:当前缓冲长度为下一个视频块选择的比特率最大化用户的质量体验 (QoE)强化学习 (RL)
集群作业调度 (CJS)图:描述作业执行阶段的依赖性和资源需求的有向无环图 (DAGs)下一个要运行的作业阶段,分配给该阶段的执行器数量最小化作业完成时间强化学习 (RL)

LLM适用于网络的三个主要挑战:

  • 大的模态差异:输入多样化与LLM单一文本输入不匹配,尽管设计提示模板进行辅助但结果仍欠佳。
  • 基于token的答案生成效率低:幻觉问题且不满足网络任务需要的高可靠性和快速响应。
  • 高适配成本:基于RL的大参数LLM实现成本过高。

NETLLM DESIGN

在这里插入图片描述

Multimodal Encoder

Feature encoder:对于不同种类的特征使用一些现有的高性能特征编码器提取特征。

Linear projection:解决提取到的特征维度与要求输入维度不等的问题。

Networking Head

相比于传统LLM,输出仅需一次推理,且输出的结果是有效的。

​ 公式说明:
特定任务的数据集 : D s l = X , Y 多模态编码器编码输入数据 : x L L M 中获取预测结果 : y ^ 网络头部计算参数更新的损失 : L s l = F s l ( y , y ^ ) 特定任务的数据集:D_{sl}={X,Y} \\ 多模态编码器编码输入数据:x \\ LLM中获取预测结果: \hat{y} \\ 网络头部计算参数更新的损失:L_{sl}=F_{sl}(y, \hat{y}) 特定任务的数据集:Dsl=X,Y多模态编码器编码输入数据:xLLM中获取预测结果:y^网络头部计算参数更新的损失:Lsl=Fsl(y,y^)

Data-Driven Low-Rank Networking Adaptation

DD-LRNA包含以下两个核心设计:

  • Data-Driven networking adaptation:DD-LRNA通过使用静态的、预先收集的数据集来优化模型,提高了训练的效率,降低了实时数据处理的复杂性和资源消耗。

    ​ 公式说明:
    多个经验轨迹: D r l = { τ 1 , … , τ ∣ D r l ∣ } 轨迹: τ = { r t , s t , a t } t = 1 T ,由奖励 r 、状态 s 和动作 a 组成 从状态 s t 预期获得的累计奖励: R t = ∑ i = t T r i ,替换 r t ( 强调未来奖励的重要性 ) 状态离散化: s t = { s t 1 , … , s t n } 动作离散化: a t = { a t 1 , … , a t m } τ = { R t , s t 1 , … , s t n , a t 1 , … , a t m } t = 1 T 随机抽样: d = { R i , s i 1 , … , s i n , a i 1 , … , a i m } i = t − w + 1 t ∈ D r l L L M 关于 d 的输出: { a ^ i 1 , . . . , a ^ i m } i = t − w + 1 t 损失函数: L r l = 1 w ∑ i = 1 w ∑ j = 1 m F r l ( a i j , a ^ i j ) 多个经验轨迹:\mathcal{D}_{rl} = \{\tau_1, \dots, \tau_{|\mathcal{D}_{rl}|}\} \\ 轨迹:\tau = \{r_t, s_t, a_t\}_{t=1}^T,由奖励r、状态s和动作a组成 \\ 从状态s_t预期获得的累计奖励:R_t = \sum_{i=t}^T r_i,替换r_t(强调未来奖励的重要性) \\ 状态离散化:s_t = \{s_t^1, \dots, s_t^n\} \\ 动作离散化:a_t = \{a_t^1, \dots, a_t^m\} \\ \tau = \{R_t, s_t^1, \dots, s_t^n, a_t^1, \dots, a_t^m\}_{t=1}^T \\ 随机抽样:d = \{R_i, s_i^1, \dots, s_i^n, a_i^1, \dots, a_i^m\}_{i=t-w+1}^{t} \in \mathcal{D}_{rl} \\ LLM关于d的输出:\{\hat{a}_i^1,...,\hat{a}_i^m\}_{i=t-w+1}^{t} \\ 损失函数:L_{rl} = \frac{1}{w} \sum_{i=1}^w \sum_{j=1}^m F_{rl}(a_i^j, \hat{a}_i^j) 多个经验轨迹:Drl={τ1,,τDrl}轨迹:τ={rt,st,at}t=1T,由奖励r、状态s和动作a组成从状态st预期获得的累计奖励:Rt=i=tTri,替换rt(强调未来奖励的重要性)状态离散化:st={st1,,stn}动作离散化:at={at1,,atm}τ={Rt,st1,,stn,at1,,atm}t=1T随机抽样:d={Ri,si1,,sin,ai1,,aim}i=tw+1tDrlLLM关于d的输出:{a^i1,...,a^im}i=tw+1t损失函数:Lrl=w1i=1wj=1mFrl(aij,a^ij)

  • Low-rank networking adaptation:冻结LLM的参数,引入了额外的低秩矩阵来近似LLM参数中所需的变化。该方法的基本思想是:调整期间的参数变化(即ΔΦ)**本质上是低秩**的。

    ​ 公式说明:
    预训练矩阵 W 0 ∈ Φ 0 ,维度为 d × k 假设 ∃ r ≪ m i n ( d , k ) 构建: A ∈ d × r , B ∈ r × k 更新优化为: W = W 0 + Δ W = W 0 + A B 预训练矩阵W₀ ∈ Φ₀,维度为d × k \\ 假设∃r≪min(d, k) \\ 构建:A∈d×r, B∈r×k \\ 更新优化为:W = W₀ + ΔW = W₀ + AB 预训练矩阵W0Φ0,维度为d×k假设rmin(d,k)构建:Ad×r,Br×k更新优化为:W=W0+ΔW=W0+AB

在这里插入图片描述

EVALUATION

Setup

三个基于学习的算法(基准线):

  • VP 的 TRACK
  • ABR 的 GENET
  • CJS 的 Decima

三个性能指标:

  • VP 的平均绝对误差(MAE):越低越好
  • ABR 的体验质量(QoE):越高越好
  • CJS 的作业完成时间(JCT):越低越好
Deep Dive
  • 预训练知识的重要性与领域特定知识的重要性
  • NetLLM 具有兼容性,适用于各种 LLM
  • 参数超过 1B 的 LLM 适合用于网络适配
  • 计算开销可以接受

DISCUSSION

多模态LLM并不一定优于单模态LLM,这一问题可以通过开发更通用的LLM(例如,支持更多模态)来缓解

NetLLM 的理念可以轻松应用于其他网络任务,还需验证 NetLLM 在适配LLM以解决更多网络问题方面的有效性。

模型压缩研究可用于减少LLM的计算开销,这些研究可以整合到NetLLM中,以减少在实际网络部署中LLM的开销。

Codes

run_plm.py

LoRA技术:引入低秩矩阵来修改网络的权重,原始的高维权重矩阵可以通过两个较小的矩阵的乘积来近似表示。

adapt

  • 初始化

    • 优化器使用 AdamW 加入了权重衰减,有助于控制模型的过拟合。
  • 学习率调度器使用 LambdaLR 动态调整学习率。

  • 训练循环

    • 每次训练后保存日志和损失
    • 固定周期后保存检查点
    • 固定周期评估模型,择优更新
  • 记录最终损失

run

  • 搭建模拟环境,设置经验池
  • 加载指定模型,创建EncoderNetwork将环境状态信息转为NN可处理的形式,创建RL策略
  • 使用裁剪和归一化处理设置奖励
plm_special
data

dataset.py:

  • discount_returns: 计算给定奖励序列的折扣累积回报
  • sample_batch: 抽批次
  • _normalize_rewards: 奖励归一化
  • _compute_returns: 计算每个时间步的折扣累积回报。
models

state_encoder.py:

  • self.fc1self.fc2 是全连接层,分别用于编码上一个比特率和当前缓冲大小。

  • self.conv3self.conv4self.conv5 是一维卷积层,用于编码过去的吞吐量、下载时间和接下来的块大小。

  • self.fc6 是全连接层,用于编码剩余块数。

  • 均使用 LeakyReLU 激活函数来增加非线性

rl_policy.py:

  • 设置多模态编码器部分
  • 定义了 OfflineRLPolicy 类的 forward 方法
  • 在给定状态、目标回报和时间步的情况下,进行动作采样

gpt2.py:

  • 模型初始化:嵌入层、Dropout层、主体层、归一化层
  • 设置早停机制

llama.py:

  • 模型初始化:嵌入层、多个解码层、规范化层(RMSNorm)
  • 设置早停机制
utils

plm_utils.py:

  • 将 Llama 模型的各个层分配到不同的设备(GPU)上
  • 加载特定的预训练语言模型
  • 向预训练模型的分词器中添加特殊令牌,并在必要时调整模型的嵌入层大小以适应新增的令牌。

utils.py:

  • 处理批数据,转换为PyTorch张量,推送到指定设备
  • 设置随机种子,确保可重复性
  • 动作转比特率
  • 从一系列日志文件中计算出平均奖励
trainer.py
  • 训练器的初始化
  • 分别定义训练一个批次和训练一个周期的函数
evaluate.py
  • 评估一个环境中的模型表现
test.py
  • 在指定的仿真环境中测试机器学习模型,主要关注模型的决策和性能。

Learning NetLLM Code Details

dataset.py

def discount_returns(rewards, gamma, scale):

折扣因子 gamma:决定未来奖励在当前的价值,较高的 gamma 值表示长远的回报对当前决策的重要性更大。

​ 对于一个完整的奖励序列 r1, r2, ... , rn,从时间 t 开始的折扣回报 Rt 可以递归地表示为:

R t = r t + γ r t + 1 + γ 2 r t + 2 + … + γ n − t r n R_t = r_t + \gamma r_{t+1} + \gamma^2 r_{t+2} + \ldots + \gamma^{n-t} r_n Rt=rt+γrt+1+γ2rt+2++γntrn
​ 其中 γ 是折扣因子,rt 是在时刻 t 获得的即时奖励。

更有效的方法是使用逆序计算
for  t = n − 1  to  1 : R t = r t + γ × R t + 1 \begin{aligned} & \text{for } t = n-1 \text{ to } 1: \\ & \quad R_t = r_t + \gamma \times R_{t+1} \end{aligned} for t=n1 to 1:Rt=rt+γ×Rt+1
假设我们有一个简单的强化学习任务,智能体需要在一系列状态中选择最佳行为以最大化累积奖励。我们收集了以下经验数据:

  • 状态列表:[S1, S2, S3, S4]
  • 对应行为:[A1, A2, A3, A4]
  • 对应奖励:[R1, R2, R3, R4]
  • 完成标志:[False, False, True, False] —— 表示第三个行为后任务完成

给定折扣因子 γ=0.9

从后向前遍历:

  • 对于第四个状态(S4),由于它后面没有完成任务,所以其折扣回报就是奖励本身,即 4。

  • 第三个状态(S3)标志了一个任务的完成,因此它的回报是当前奖励加上后续折扣回报的 (0.9) 倍。计算如下:
    R 3 = 3 + 0.9 × 0 = 3 R3 = 3 + 0.9 \times 0 = 3 R3=3+0.9×0=3

  • 第二个状态(S2)的计算需要包括从这个状态开始直到任务完成的所有折扣奖励。计算如下:
    R 2 = 2 + 0.9 × 3 = 4.7 R2 = 2 + 0.9 \times 3 = 4.7 R2=2+0.9×3=4.7

  • 第一个状态(S1)的折扣回报包括从状态一开始直到任务完成的所有奖励:
    R 1 = 1 + 0.9 × 4.7 ≈ 5.23 R1 = 1 + 0.9 \times 4.7 \approx 5.23 R1=1+0.9×4.75.23

结果 折扣回报列表将会是:[5.23, 4.7, 3, 4]

缩放因子 scale:用于将回报值归一化,保持数值稳定性。

def sample_batch(self, batch_size=1, batch_indices=None):

假设我们的经验池包含以下数据,并且已经通过前面的步骤计算了折扣回报和时间步:

  • 状态:[[S1], [S2], [S3], [S4]]
  • 行为:[[A1], [A2], [A3], [A4]]
  • 折扣回报:[[G1], [G2], [G3], [G4]]
  • 时间步:[[0], [1], [2], [3]]

现在我们调用 sample_batch(batch_size=2) 方法:

  1. 由于没有提供 batch_indices,方法内部将随机选择两个索引,假设为1和3。
  2. 使用索引1和3从数据集中获取数据:
    • 索引1:状态 [S2],行为 [A2],折扣回报 [G2],时间步 [1]
    • 索引3:状态 [S4],行为 [A4],折扣回报 [G4],时间步 [3]
  3. 将获取的数据分别添加到批次数据列表中:
    • batch_states 包含 [[S2], [S4]]
    • batch_actions 包含 [[A2], [A4]]
    • batch_returns 包含 [[G2], [G4]]
    • batch_timesteps 包含 [[1], [3]]
  4. 最终,方法返回这四个列表作为一批数据。
        while episode_start < self.exp_pool_size:
            try:
                episode_end = self.dones.index(True, episode_start) + 1
            except ValueError:
                episode_end = self.exp_pool_size
            self.returns.extend(discount_returns(self.rewards[episode_start:episode_end], self.gamma, self.scale))
            self.timesteps += list(range(episode_end - episode_start))
            episode_start = episode_end
        assert len(self.returns) == len(self.timesteps)

假设我们的经验池数据如下:

  • 状态:[S1, S2, S3, S4, S5]
  • 行为:[A1, A2, A3, A4, A5]
  • 奖励:[R1, R2, R3, R4, R5]
  • 完成标志(dones):[False, False, True, False, False]

在这个例子中,第三个时间步后有一个 True 值,表示一个episode结束。但是,最后一个时间步虽然没有 True 值,也会被视为一个episode的结束(因为已经是经验池的最后)。因此,代码会计算两个episode的折扣回报:

  • 第一个episode从S1到S3,计算 R1R3 的折扣回报。
  • 第二个episode从S4到S5,计算 R4R5 的折扣回报。

state_encoder.py

    def __init__(self, conv_size=4, bitrate_levels=6, embed_dim=128):

bitrate_levels : 指视频可以被编码和传输的不同质量级别,一个视频可以有多个分辨率和码率的版本,每个版本对应一个比特率级别。

embed_dim : 嵌入维度的大小,它定义了网络输出特征的维度。用于初始化多个全连接层和卷积层的输出通道数,确保所有特征映射到相同维度的空间。

rl_policy.py

        # =========== multimodal encoder (start) ===========
    	# 存储状态编码器和状态特征维度
        self.state_encoder = state_encoder
        self.state_feature_dim = state_feature_dim
        
        # 创建一个嵌入层,用于将时间步转换为固定维度的向量。
        # max_ep_len + 1 是嵌入矩阵的大小,确保可以嵌入从 0 到 max_ep_len 的任何时间步
        self.embed_timestep = nn.Embedding(max_ep_len + 1, plm_embed_size).to(device)
        
        # 线性层,将单一值(如返回值和动作值)转换为与PLM相同维度的向量。
        self.embed_return = nn.Linear(1, plm_embed_size).to(device)
        self.embed_action = nn.Linear(1, plm_embed_size).to(device)
        
        # 多状态特征的嵌入层,将不同的状态特征转换为PLM的嵌入空间
        self.embed_state1 = nn.Linear(state_feature_dim, plm_embed_size).to(device)
        self.embed_state2 = nn.Linear(state_feature_dim, plm_embed_size).to(device)    
        self.embed_state3 = nn.Linear(state_feature_dim * (6 - conv_size + 1), plm_embed_size).to(device)   
        self.embed_state4 = nn.Linear(state_feature_dim * (6 - conv_size + 1), plm_embed_size).to(device)   
        self.embed_state5 = nn.Linear(state_feature_dim, plm_embed_size).to(device)
        self.embed_state6 = nn.Linear(state_feature_dim, plm_embed_size).to(device)    
		
        # 规范化嵌入向量
        self.embed_ln = nn.LayerNorm(plm_embed_size).to(device)
        # =========== multimodal encoder (end) ===========
    def forward(self, states, actions, returns, timesteps, attention_mask=None):

嵌入向量的意义:是高维空间中的点,它们能够以密集的形式表示原始数据的信息。每个状态、动作、回报和时间步被转换为嵌入向量,这些向量是通过学习得到的,能够捕捉输入数据的重要特征。时间步的嵌入加到其他嵌入上,可以帮助模型理解序列中每个元素的位置或顺序,这对于处理序列数据非常重要。

时间步嵌入的原因:告诉模型序列中每个状态或动作发生的具体时刻、帮助模型捕捉序列中的时间动态、模型可以更容易地学习到不同时间步骤之间的依赖关系(如需要基于前一个状态或动作来预测下一个动作的场景)、自回归模型时,时间步嵌入可以增强模型对序列的理解。

堆叠嵌入向量:“将回报、状态和动作嵌入按序堆叠起来”意味着将这些不同的嵌入向量按照它们在序列中的逻辑顺序(如先是回报,再是状态,最后是动作)连续排列。这种排列方式使得整个输入序列像是一个自然语言处理中的长句子,每个嵌入向量都是句子中的一个“词”,这样预训练模型就可以按照这个序列来预测下一个最可能的“词”(在这里是动作)。

这里的步骤可以总结为以下几点:

  1. 特征嵌入:将动作、回报、和时间步转换成嵌入向量。这些嵌入向量通过专门的神经网络层(如nn.Linearnn.Embedding)生成,以将单一或者少量维度的数据映射到更高维的表示空间中。
  2. 时间步嵌入:时间步的嵌入类似于自然语言处理中的位置嵌入(positional embeddings),它帮助模型理解序列数据中各个元素的顺序。这里,时间步的嵌入被加到了动作嵌入和回报嵌入上,增强了时间信息在决策过程中的作用。
  3. 状态特征编码:状态特征通过一个专门的状态编码器(state_encoder)处理,目的是从原始状态数据中提取有用的特征表示。
  4. 特征融合:将所有的嵌入向量(包括动作、回报、时间步以及从多维状态数据中提取的多个嵌入结果)按序堆叠成一个大的序列。这个序列捕捉了决策点的全面信息,为下一步的预测提供了必要的上下文。
  5. 动作预测:堆叠的嵌入向量被送入一个预训练的语言模型(PLM),以处理序列化的数据并产生预测输出。输出再经过一个特定的网络层(如动作头)来预测最优动作。
    def forward(self, states, actions, returns, timesteps, attention_mask=None):

这里的步骤可以总结为以下几点:

  1. 堆叠以前的状态、动作和回报特征

  2. 处理目标回报和时间步:生成回报和时间步的嵌入向量,并将时间步嵌入向量加到回报嵌入向量上。

  3. 处理状态:输入的状态通过状态编码器转换为特征表示,每个特征都会被进一步处理成嵌入向量,状态特征的嵌入向量与时间嵌入向量相加,以整合时间信息。

  4. 堆叠回报、状态和先前的嵌入向量:把以前的状态等信息和本次的信息堆叠在一起送入模型进行预测么

  5. 通过预训练语言模型处理堆叠的嵌入:堆叠的嵌入向量通过层归一化,然后输入到PLM中

  6. 预测动作

  7. 更新队列

gpt2.py

self.wte = nn.Embedding(config.vocab_size, self.embed_dim)

嵌入层:把一个单词(最大单词数为vocab_size)转换为一个固定长度(embed_dim)的向量,方便后续处理。

llama.py

def _prepare_decoder_attention_mask(self, attention_mask, input_shape, inputs_embeds, past_key_values_length):

注意力掩码(Attention Mask):用于控制模型应该关注(即"注意")序列中的哪些部分。在实现上,注意力掩码通常是一个与输入序列形状相匹配的张量,在需要遮蔽的位置上的值非常小(如负无穷),而其他位置的值为0。这样,在计算softmax归一化的注意力权重时,遮蔽位置的权重会接近于0,从而在实际的注意力计算中被忽略。

plm_utils.py

    tokenizer = model_class.tokenizer.from_pretrained(model_path) 
    print("If tokenizer is loaded: ",tokenizer.encode("hello world"),"\n")

    wrapper = model_class.wrapper
    model, tokenizer = add_special_tokens(model, tokenizer, specials_to_add=specials_to_add)

**分词器(Tokenizer)**是用于将文本数据转换为模型可以理解的格式的工具。在自然语言处理(NLP)任务中,模型无法直接处理原始文本,需要将文本转换为数值形式。分词器负责这一转换过程,通常包括以下步骤:

  1. 分词(Tokenization):将连续的文本字符串分割成较小的单元,例如单词、子词或字符。
  2. 编码(Encoding):将分割后的单元转换为整数索引。
  3. 添加特殊标记:例如,句子开始(BOS)和结束(EOS)标记,或用于填充(Padding)的标记。

示例: 假设我们有一个简单的英文句子:“Hello, world!”。使用一个基本的分词器,该句子可能会被分割为 [“Hello”, “,”, “world”, “!”],然后每个分词根据词汇表被编码为一个整数,例如 [687, 12, 523, 3]。

分词器嵌入层之间的工作流程是紧密连接的,主要步骤如下:

  1. 分词器(Tokenizer):首先使用分词器处理文本。分词器将文本分解成更小的单元(如词、子词片段或字符),并将这些单元转换为数字ID。这个过程通常包括清洗文本、分割词汇和映射到预定义词汇表的索引。
  2. 嵌入层(Embedding Layer):接着,这些数字ID被送入嵌入层。嵌入层将每个数字ID转换为一个密集的向量表示。这些向量是在模型训练过程中学习得到的,能够捕捉单词之间的语义和语法关系。

**包装器(Wrapper)**是一个封装层,用于处理模型和其它组件(如分词器)之间的交互。在使用预训练模型(如BERT、GPT等)进行特定任务时,直接与原始模型交互可能不够方便或不符合任务需求。包装器提供了一种灵活的方式来调整模型的输入输出,以适应特定任务的需求。

示例: 假设我们使用BERT模型进行情感分类任务。原始BERT模型输出一系列与输入文本同样长度的隐藏状态,但情感分类只需要基于整个输入的一个总体判断。这时,可以使用一个包装器来提取BERT输出的第一个token(通常对应于特殊的分类标记[CLS])的隐藏状态,并将其传递给一个分类层,最终输出正面或负面的情感判断。

utils.py

def calc_mean_reward(result_files, test_dir, str, skip_first_reward=True):
    matching = [s for s in result_files if str in s]
    reward = []
    count = 0
    for log_file in matching:
        count += 1
        first_line = True
        with open(test_dir + '/' + log_file, 'r') as f:
            for line in f:
                parse = line.split()
                if len(parse) <= 1:
                    break
                if first_line:
                    first_line = False
                    if skip_first_reward:
                        continue
                reward.append(float(parse[7]))
    print(count)
    return np.mean(reward)

跳过每个文件的第一行奖励值skip_first_reward=True(可能因为第一行是初始化值或者只是表头)

举例:

file1.txt

time step reward
0     1    2.0
1     2    3.5
2     3    3.0

file2.txt

time step reward
0     1    1.0
1     2    4.0
2     3    5.0
result_files = ["file1.txt", "file2.txt"]
test_dir = "/path/to/logfiles"
keyword = "file"

mean_reward = calc_mean_reward(result_files, test_dir, keyword, skip_first_reward=True)

print("Average Reward:", mean_reward)
  1. 匹配文件matching = ['file1.txt', 'file2.txt'] - 匹配所有包含"file"的文件。

  2. 初始化奖励列表和计数器reward = [] count = 0

  3. 逐文件处理

    • 对于file1.txt

      • 读取并跳过第一行(如果skip_first_reward=True)。
      • 从第二行开始,解析每行的第三个值作为奖励,并添加到reward列表。
      • reward更新为 [3.5, 3.0]
    • 对于file2.txt

      • 类似地处理,跳过第一行。
      • reward更新为 [3.5, 3.0, 4.0, 5.0]
  4. 计算平均值并返回

    • np.mean(reward) 计算出 3.875

输出:

Average Reward: 3.875

trainer.py

        for step, batch in enumerate(self.dataloader):
            train_loss = self.train_step(batch)
            train_losses.append(train_loss.item())

            # perform gradient accumulation update
            # 将损失除以梯度累积步数,用于实现梯度累积
            train_loss = train_loss / self.grad_accum_steps 
            train_loss.backward()
            
            # 梯度裁剪,防止梯度爆炸
            torch.nn.utils.clip_grad_norm_(self.model.parameters(), .25)
            if ((step + 1) % self.grad_accum_steps == 0) or (step + 1 == dataset_size):
                self.optimizer.step()
                self.optimizer.zero_grad(set_to_none=True)
                
                # 如果存在学习率调度器,更新学习率。
                if self.lr_scheduler is not None:
                    self.lr_scheduler.step()

梯度累积:这种方法允许模型以比物理内存限制更大的有效批量大小进行训练。模型参数的更新不是在每个批次后立即进行,而是在多个批次的梯度被累加之后进行。这意味着每个批次计算得到的梯度不会立即用于更新参数,而是累积到一定数量后,再统一进行参数更新。这样做可以模拟更大批量数据的训练效果,有助于稳定训练过程并提高模型性能。

梯度裁剪:指在训练过程中梯度的值变得非常大,以至于更新后的模型参数使得模型变得不稳定。梯度裁剪通过设定一个阈值,将那些超过这个阈值的梯度裁剪到一个最大值,从而控制梯度的最大更新量。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

辰chen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值