最近秋招发放Offer已高一段落。
不同以往的是,当前职场环境已不再是那个双向奔赴时代了。求职者在变多,HC 在变少,岗位要求还更高了。
最近,我们又陆续整理了很多大厂的面试题,帮助一些球友解惑答疑,分享技术面试中的那些弯弯绕绕。
喜欢本文记得收藏、关注、点赞。更多实战和面试交流,文末加入我们
技术交流
今天分享一些京东算法岗一面面经。
1、直接开始八股
2、Transformer 中为什么用LN不用BN?
在Transformer模型中,选择使用层归一化(LN)而不是批归一化(BN)的原因主要基于以下几点:
-
序列数据的特性:Transformer模型主要用于处理自然语言处理(NLP)任务,这些任务中的输入数据通常是变长序列。由于BN是在整个批次上计算均值和方差,当序列长度不一致时,BN的效果可能会受到影响。而LN是在每个样本的每个层内部进行归一化,不受序列长度的影响,因此更适合处理NLP任务中的变长序列数据。
-
模型训练的稳定性:在Transformer中,每个位置(token)的表征都是独立的,并且随着层数的增加,这些表征的变异性可能会增加。LN通过在每个样本的每个层内部进行归一化,有助于保持表征的稳定性,从而有助于模型学习更有效的注意力机制。相比之下,BN可能会因为批次中不同样本的变异性而导致训练过程中的不稳定。
-
计算效率与实现:LN的计算相对简单,因为它只需要在每个样本的每个层内部进行归一化,而不需要跨样本进行计算。这有助于减少计算成本,并提高模型的训练效率。此外,LN的实现也相对容易,因为它不需要额外的存储来保存整个批次的统计数据。
综上所述,Transformer中选择使用LN而不是BN,主要是基于LN对序列数据的适应性、模型训练的稳定性以及计算效率等方面的优势。这些优势使得LN成为Transformer处理NLP任务时的理想选择。
3、图像中 BN 是怎么用的?
在图像处理领域,批量归一化(Batch Normalization,简称BN)的使用与在自然语言处理(NLP)任务中的Transformer模型有所不同,但基本思想类似,都是为了提高模型的训练效率和稳定性。
- 输入数据归一化:
- 在将图像数据输入到神经网络之前,通常会对图像进行预处理,包括调整图像大小、归一化像素值等。虽然这一步不是严格意义上的BN,但它为后续的BN层提供了更好的输入数据。
- 卷积层后的BN:
- 在图像处理中,卷积层是提取图像特征的关键部分。在卷积层之后,通常会接一个BN层来对卷积层的输出进行归一化处理。这样做的好处是可以减少卷积层输出数据的内部协变量偏移(Internal Covariate Shift),使后续层的学习更加稳定。
- BN层的参数学习:
- BN层包含两个可学习的参数:缩放因子(scale)和偏移因子(shift)。这两个参数在训练过程中通过反向传播算法进行更新,以调整归一化后的数据分布,使其更适合后续层的处理。
- 与其他层的组合:
- 在图像处理的神经网络中,BN层通常与卷积层、激活函数层(如ReLU)等组合使用。这种组合可以进一步提高模型的表达能力和训练效率。
4、在 NLP 中如果句子长度不一致,用 BN 会有什么后果?
在NLP中,如果句子长度不一致,使用批归一化(BN)可能会带来以下问题:
-
归一化效果受影响:BN需要在整个批次上计算均值和方差,但句子长度不一致可能导致这些统计量不准确。
-
计算复杂:处理不同长度的句子会增加计算的复杂性,可能需要额外的填充或截断操作。
-
模型性能可能下降:BN可能会改变数据的分布,这种改变可能与NLP任务的特性不符,导致模型性能下降。
因此,在NLP任务中,层归一化(LN)通常被认为是更适合的归一化方法,因为它不受句子长度的影响,能更好地处理变长序列数据。
5、给定三维矩阵bsz * seq_len * dim,BN和LN分别作用在哪个维度?
在给定三维矩阵bsz * seq_len * dim中,BN(批归一化)和LN(层归一化)分别作用在不同的维度上。
具体来说:
-
BN(批归一化):BN通常作用于特征维度(即dim维度)上,但它是基于整个批次(即bsz个样本)来计算的。也就是说,BN会对每个特征(dim)在整个批次(bsz)的所有样本(seq_len个token)上进行归一化。然而,在NLP任务中,由于句子长度(seq_len)可能不一致,直接使用BN可能会带来一些问题,如前面所述。
-
LN(层归一化):LN则是对每个样本(bsz中的一个样本)的每个层(在这里可以理解为seq_len * dim的二维矩阵,即一个句子的所有token的表征)进行归一化。也就是说,LN会在每个样本的每个层内部(即seq_len和dim两个维度上)计算均值和方差,并进行归一化。这种方式不受句子长度的影响,因此更适合处理NLP任务中的变长序列数据。
综上所述,BN和LN在三维矩阵bsz * seq_len * dim上分别作用于不同的维度:BN主要作用于特征维度(dim),但基于整个批次(bsz)来计算;而LN则对每个样本的每个层(seq_len * dim)进行归一化。
6、已知bsz seq_len dim head,参数量是多少,和哪几个参数有关?
在Transformer模型中,参数量主要与以下几个因素有关:
-
嵌入维度(dim):这是模型中最基本的参数之一,决定了输入嵌入向量的大小。嵌入维度越大,模型的表示能力通常越强,但参数量也会相应增加。
-
注意力头的数量(head):在自注意力机制中,模型会将输入分成多个头(即多个子空间)进行处理。每个头都有自己的查询、键和值矩阵,因此头的数量越多,参数量也会相应增加。
-
前馈神经网络(FFN)的隐藏层维度:前馈神经网络是Transformer模型中的另一个重要组成部分,其隐藏层维度通常与嵌入维度有关(例如,可能是嵌入维度的4倍)。隐藏层维度越大,模型的表示能力越强,但参数量也会相应增加。
-
其他参数:除了上述主要参数外,Transformer模型还可能包含一些其他参数,如层归一化中的参数、残差连接中的参数等。这些参数的数量相对较少,但在计算总参数量时也需要考虑。
综上所述,Transformer模型的参数量主要与嵌入维度、注意力头的数量、前馈神经网络的隐藏层维度以及其他一些参数有关。由于这些参数之间存在一定的关系(例如,隐藏层维度通常是嵌入维度的倍数),因此在实际应用中,可以通过调整这些参数来控制模型的复杂度和参数量。
需要注意的是,上述分析是基于Transformer模型的一般结构和参数计算方式进行的。在实际应用中,不同的实现方式和优化策略可能会导致参数量的差异。因此,在具体计算参数量时,需要参考具体的模型实现和代码。
7、带有多个注意力头的注意力机制计算过程
带有多个注意力头的注意力机制,即多头注意力(Multi-Head Attention)机制,是Transformer架构中的关键组成部分。
-
输入张量:假设输入张量为X,其形状通常为(batch_size, seq_len, d_model),其中batch_size表示批次大小,seq_len表示序列长度,d_model表示嵌入维度。
-
线性变换:通过三个不同的线性层(或称为全连接层),将输入X转换为查询(Query)、键(Key)和值(Value)矩阵。这三个线性层的权重矩阵分别为WQ、WK和WV。
-
分割:将查询、键和值矩阵沿着最后一个维度分割成h份,每份的维度为dk(通常d_model=h*dk)。这样,就得到了h个注意力头。
-
并行计算:对于每个注意力头,独立地计算注意力分数和注意力输出。注意力分数的计算通常使用缩放点积注意力机制,即计算查询和键的点积,然后除以√dk进行缩放,最后应用softmax函数得到注意力权重。使用这些权重对值进行加权求和,得到每个头的注意力输出。
-
拼接:将所有注意力头的输出沿着某个维度(通常是最后一个维度)拼接起来,形成一个新的矩阵。
-
线性变换:将拼接后的结果通过另一个线性变换层进行投影,得到最终的多头注意力输出。这个线性变换层的权重矩阵为WO。
其计算过程可以概括为:输入处理(将输入转换为查询、键和值矩阵)→ 分割与并行计算(在每个子空间中独立计算注意力)→ 拼接与线性变换(将所有头的输出拼接并进行线性变换)。
8、说出pytorch中维度变换的函数?
PyTorch中维度变换函数:
-
**
reshape
/view
**: 改变张量的形状,不改变数据。view
要求新形状的总元素与原始相同。 -
**
squeeze
**:移除所有维度为1的轴。 -
**
unsqueeze
**:在指定位置插入大小为1的新维度。 -
**
permute
**:重新排列张量的维度。 -
**
transpose
**:交换张量的两个维度。 -
**
cat
**:沿着指定维度连接张量列表。 -
**
stack
**:沿着新维度连接张量列表,创建新的维度。 -
**
split
**:根据大小或数量分割张量。 -
**
chunk
**:将张量分割成相等大小的块。 -
**
narrow
**:返回张量在指定维度的子张量。
这些函数允许用户灵活地操作和变换张量的形状,是PyTorch处理多维数据的重要工具。
9、显存OOM,参数,ZERO,vllm,梯度累积,优化器,混合精度
10、讲一下实习经历
11、长度外推技术
12、觉得自己做得最好的点是什么
13、使用华为的框架和显卡进行SFT有没有遇到什么问题
14、LongLoRA和LoRA的区别?
LongLoRA:
-
应用领域:自然语言处理(NLP),特别是提高大型语言模型的上下文能力。
-
技术原理:一种微调方法,结合稀疏局部关注和上下文扩展技术,旨在降低计算成本。
LoRa(通信领域):
-
应用领域:物联网(IoT),实现远距离、低功耗的无线通信。
-
技术原理:一种低功耗远距离无线电无线标准,通过扩频调制机制实现远距离传输。
简而言之,LongLoRA是NLP领域的微调技术,而LoRa是物联网领域的无线通信技术。
15、算法题:返回第K大的数,要求比快排更快
要求比快速排序(QuickSort)更快的算法来返回第 K 大的数,首先要考虑快速排序的时间复杂度。快速排序的平均时间复杂度是 O(nlogn)O(n_log_n),但是在最坏情况下它可能会退化到 O(n2)O(_n_2)。不过,快速选择算法(Quickselect)能够解决这个问题,且在平均情况下时间复杂度为 O(n)O(n)。
快速选择是一个基于快速排序的思想来找到数组中的第 K 大元素。与快速排序不同,快速选择只会对数组的部分进行排序(在找到第 K 大元素之后,其他部分并不需要排序)。
"""
算法步骤:
1、选择一个基准元素:选择数组中的一个随机元素作为基准。
2、分区操作:将数组分为两部分:
一部分包含所有大于基准的元素(这一部分的元素都比基准大)。
另一部分包含所有小于基准的元素。
3、根据 K 的位置决定递归方向:
如果基准元素的位置正好是 K,则基准元素即为答案。
如果基准元素的位置大于 K,则递归查找左半部分。
如果基准元素的位置小于 K,则递归查找右半部分。
"""
import random
def quickselect(nums, left, right, k):
"""
返回数组 nums 中第 k 大的元素
"""
if left == right:
return nums[left]
pivot_index = random.randint(left, right)
pivot_index = partition(nums, left, right, pivot_index)
# 目标位置
if k == pivot_index:
return nums[k]
elif k < pivot_index:
return quickselect(nums, left, pivot_index - 1, k)
else:
return quickselect(nums, pivot_index + 1, right, k)
def partition(nums, left, right, pivot_index):
pivot_value = nums[pivot_index]
# Move pivot to end
nums[pivot_index], nums[right] = nums[right], nums[pivot_index]
# Move all smaller elements to the left
store_index = left
for i in range(left, right):
if nums[i] > pivot_value:
nums[store_index], nums[i] = nums[i], nums[store_index]
store_index += 1
# Move pivot to its final place
nums[right], nums[store_index] = nums[store_index], nums[right]
return store_index
def find_kth_largest(nums, k):
"""
找到数组中第 k 大的元素
"""
# Convert k to index in the zero-based index system
k = len(nums) - k
return quickselect(nums, 0, len(nums) - 1, k)
平均时间复杂度:快速选择的平均时间复杂度是 O(n)O(n),因为每次递归大约会将数组分成两半。每次递归调用处理的元素数量减少,因此总的期望时间复杂度是线性的。
最坏时间复杂度:最坏情况下(比如每次选择的基准都最小或最大),时间复杂度为 O(n2)O(_n_2)。但是可以通过随机化选取基准来减少最坏情况发生的概率,从而使得平均时间复杂度为 O(n)O(n)。
如果你要求一个比快速排序更快的算法来返回第 K 大的数,快速选择算法(Quickselect)通常是最优解。它在平均情况下能够在线性时间 O(n)O(n) 内找到第 K 大的数。