淘天暑期实习计算机视觉面试凉经
面试凉经
估计没有二面了,这次被拷打的狠狠的,估计是无了,第一次视觉面,没有准备相关内容,第二天去看,果然已经流程结束
自我介绍
和前面一样先自我介绍。
项目拷打
这次我投的计算机视觉算法,面试的比较对口,面试官就问的很细,面试首先是问了几个项目的内容,我前面做的任务主要是通用视觉目标检测、姿态估计、分割等这些的项目,这些项目都没什么深度,主要是用,然后问的很细,就很难受,被拷打出来了没做啥有深度的内容,主要是应用,这块就不太行了。前面的面试面试官不是研究视觉的,就只大致问问做项目过程中遇到的难点,困难如何解决的,这次要先把做的任务说清楚,不清楚就会继续不断追问,然后说清楚做了什么,涉及较难的部分还会重点追问。但是这部分我往往都是只应用,并没有结构上的创新,然后具体用的什么模型,如何训练,如何处理,等等都会追问,问的非常细,项目虽然是自己做的,但是很多东西也没怎么想到,就非常难受。这里面除了一个配准的专利面试官可能没做过没问,其他所有项目比赛都非常仔细的抠细节。
现在想想,前面做过的项目也没有系统的整理里面的亮点和创新点等等,而且做的时候也很多时候只是用现有的技术做了,但是没有去想过创新点,含金量就不太够,还是要把做过的东西包装一下。
简单的数据处理脚本
然后面试官说让做题,首先是一个简单的路径字符串列表变成文件树的任务,这个非常简单的,这种类似的见得多了,但是当时太紧张了,然后这个我就没写出来,实在是不应该,其实另外一个写不出来的问题在于,平时写代码都有Github Copilot辅助,基本都是只需要写个大致的思路,辅助编程就能自动补全了,具体某个函数名字和参数什么的真不怎么记住,这个也是一个问题,以后要多写多记住一些基础的东西。
具体题目是:
1 | news/car/suv |
1 | def tree(str_list): |
logistic回归
让说一下logistic回归,我就说了下公式,然后说前些年用,因为方便求导,简单说了下,具体用来更新模型的参数没说。这相关的算法也是学过的,但是要清晰讲出来还是要准备的。
手撕注意力
然后让写一个attention的公式,然后也没写出来,就简单说了下原理,肯定是不行的,这种都是算法的八股,我应该提前准备下的。
chatgpt答案
注意力机制是一种让模型能够在处理信息时自动地聚焦于最重要信息的技术。在深度学习中,注意力机制通常用于序列模型,特别是在自然语言处理领域。最常见的注意力机制是由 Vaswani 等人在 2017 年提出的“Scaled Dot-Product Attention”。
注意力机制的数学公式
基础的注意力机制可以表示为三个核心组件:查询(Query),键(Key),值(Value)。给定查询 ( Q ),键 ( K ),和值 ( V ),注意力权重计算通常包括以下步骤:
注意力得分(Score)计算:
[ \text{Score}(Q, K) = QK^T ]缩放(Scaling):
[ \text{Scaled Score} = \frac{\text{Score}}{\sqrt{d_k}} ]
其中 ( d_k ) 是键向量的维度,这一步骤是为了避免计算中的梯度过小。Softmax 归一化:
[ \text{Attention Weights} = \text{softmax}(\text{Scaled Score}) ]输出计算:
[ \text{Output} = \text{Attention Weights} \cdot V ]
代码实现
在 Python 中,可以使用 PyTorch 这样的深度学习框架来实现注意力机制。下面是一个简单的 Scaled Dot-Product Attention 的实现示例:
1 | import torch |
这段代码实现了 Scaled Dot-Product Attention,其中 Q
, K
, V
是输入的张量,可来自相同的或不同的输入源。mask
参数用于可选的掩码操作,通常用在处理变长序列时忽略填充项。注意力权重通过点积、缩放、Softmax 计算得出,最后使用这些权重对值 V
进行加权求和,得到每个查询的输出。
手撕Attention
1 | class Attention(nn.Module): |
MLP的代码
1 | class Mlp(nn.Module): |
扩散模型
然后问扩散模型知道不,然后我说没做过,这里也是改准备下的,毕竟这么著名的模型,我虽然大致了解原理还有DDPM什么的,但是如果认真的讲出来,不成体系,我也就没敢说,这块还是要准备下。
chatgpt回答
扩散模型是一种新兴的深度学习方法,特别是在生成模型领域中表现出色。这种模型基于随机过程理论,特别是扩散过程,通过逐步增加随机性来转换数据,然后再逐步学习如何逆转这一过程,从而生成高质量的数据样本。这一方法在图像和声音生成中尤其流行。
扩散模型的基本原理
扩散模型包括两个主要阶段:正向过程(forward process)和反向过程(reverse process)。
正向过程(噪声添加过程):
在正向过程中,模型逐步地将噪声引入到原始数据中。这通常通过多个时间步骤实现,每个时间步骤都会向数据添加更多的高斯噪声,直到数据完全转变为纯噪声。这个过程可以用以下等式表示:
[
x_{t} = \sqrt{\alpha_t} x_{t-1} + \sqrt{1-\alpha_t} \epsilon_t
]
其中,( x_t ) 是在时间 ( t ) 的数据,( \alpha_t ) 是时间步骤中噪声的比例,而 ( \epsilon_t ) 是从标准正态分布中采样的噪声。反向过程(噪声去除过程):
反向过程旨在学习如何从纯噪声数据逐步恢复出原始数据。这是通过一个参数化的神经网络模型实现的,该模型试图预测给定噪声数据的前一时间步骤的状态。反向过程的目标是逐渐减少数据中的噪声,最终恢复出接近原始数据的状态。
训练扩散模型
扩散模型的训练通常依赖于变分下界(ELBO,Evidence Lower Bound)的优化。模型的目标是最小化重构数据和原始数据之间的差异,同时也优化生成数据的概率分布。训练过程需要大量的计算资源,因为它涉及到大量的正向和反向模拟步骤。
FM算法
然后问机器学习算法FM算法,然后我说不知道,这个我还真没听说过,查了下,是推荐的算法,因子分解机,真不懂,现学记住吧,chatgpt说:
chatgpt回答
FM(Factorization Machines,因子分解机)是一种广泛用于推荐系统和预测任务的机器学习算法。它被设计用来处理具有极大稀疏性的数据集,特别是在用户-物品交互数据中常见的那种稀疏性。
基本原理: 因子分解机模型主要是为了解决传统线性模型(如线性回归、逻辑回归)在处理大规模稀疏数据集时的不足。在传统的线性模型中,每个特征的权重是独立学习的,这使得模型难以捕捉特征间的交互作用。FM通过引入隐向量来为每个特征学习潜在的交互效应,这样可以有效地捕捉到特征之间的相互作用。
数学表示: 给定一个包含$n$个特征的数据集,FM模型的预测$\hat{y}$由下式给出:
$$
\hat{y}(\mathbf{x}) = w_0 + \sum_{i=1}^{n}w_i x_i + \sum_{i=1}^{n} \sum_{j=i+1}^{n} \langle \mathbf{v}_i, \mathbf{v}_j \rangle x_i x_j
$$
其中:
- $w_0$ 是模型的全局偏置。
- $w_i$ 是第$i$个特征的权重。
- $\mathbf{x}$ 是输入特征向量。
- $\mathbf{v}_i$ 和 $\mathbf{v}_j$ 是特征$i$和$j$的隐向量,隐向量的维度$k$通常远小于特征数量$n$。
- $\langle \mathbf{v}_i, \mathbf{v}_j \rangle$ 表示特征$i$和$j$的隐向量的点积,用于模拟这两个特征的交互效果。
优势:
高效性:虽然FM模型包含二阶特征交互项,但是它可以在$O(kn)$的时间复杂度内完成计算,其中$k$是隐向量的维度,$n$是特征的数量。这种高效性主要来源于隐向量的点积运算方式,使得算法适用于大规模的数据集。
灵活性:FM模型不仅适用于任何实数特征,还可以很容易地扩展到高阶交互,如三阶或更高阶的交互。
泛化能力:由于特征间的交互是通过低维隐向量学习的,FM可以很好地泛化到新的、未见过的特征组合,尤其是在数据稀疏的场景下。
应用场景:
- 推荐系统:FM广泛用于推荐系统,能有效处理用户和物品的特征交互。
- 点击率预测:在线广告和搜索引擎优化中,FM可以用来预测用户点击广告或搜索结果的概率。
- 排名和分类任务:通过适当的目标函数和优化方法,FM可以适用于各种排名和分类问题。
因子分解机因其在处理稀疏数据和特征交互中的优势而被广泛研究和应用。
手写交叉熵损失
然后让写CrossEntropy,我还是没写出来,然后简单说了下公式的思想,
chatgpt回答
交叉熵损失(Cross-Entropy Loss)是用于衡量模型预测概率分布与真实标签概率分布之间的差异的一个常用损失函数。它在分类任务中特别常见,尤其是在处理二分类或多分类问题时。
交叉熵损失的公式
对于二分类问题,交叉熵损失可以表示为:
[ L = -\frac{1}{N} \sum_{i=1}^N [y_i \log(\hat{y}_i) + (1 - y_i) \log(1 - \hat{y}_i)] ]
其中:
- ( N ) 是样本的数量。
- ( y_i ) 是第 ( i ) 个样本的真实标签,通常为 0 或 1。
- ( \hat{y}_i ) 是模型预测第 ( i ) 个样本为正类的概率。
对于多分类问题,公式扩展为:
[ L = -\frac{1}{N} \sum_{i=1}^N \sum_{c=1}^C y_{i,c} \log(\hat{y}_{i,c}) ]
其中:
- ( C ) 是类别的总数。
- ( y_{i,c} ) 是一个指示变量,如果样本 ( i ) 属于类别 ( c ),则为 1,否则为 0。
- ( \hat{y}_{i,c} ) 是模型预测第 ( i ) 个样本属于类别 ( c ) 的概率。
代码实现
如果不使用库函数,可以手动实现交叉熵损失函数。这里我们将以 Python 为例展示如何实现多分类交叉熵损失函数。这将涉及到计算模型输出的对数概率,然后根据真实标签计算损失。
二分类交叉熵损失
首先,我们从较简单的二分类交叉熵损失开始:
1 | import numpy as np |
多分类交叉熵损失
对于多分类问题,我们通常使用独热编码的方式来表示真实的标签,然后计算每个类别的交叉熵损失:
1 | def categorical_cross_entropy_loss(y_true, y_pred): |
在这些示例中,y_pred
中的值必须是有效的概率分布,即所有预测概率的和应为1,且各概率值应在0到1之间。在实际应用中,模型的输出通常通过 softmax 函数来保证这一点。np.clip
用于防止数值问题,如计算对数时输入值为0。这些实现提供了对交叉熵损失计算的直接控制,避免了依赖外部库函数的需求。
U-Net相关
我说研究方向主要是医学影像分割分类,然后面试官说前面做的这些和这个也没什么关系,然后问医学影像出了什么成果,然后我也没,然后问U-Net,我说了下,然后问U-Net的原理,我说了下,然后后上下采样是如何做的,残差连接是如何做的,我说是拼接,这块要说原理我也是说的不太清楚,也没系统准备过。
然后问那个医学影像的竞赛,说用到nnUNet了,然后介绍下,也说不清。
然后问科研医学影像现在做到什么阶段了,我就说是分割分类任务的大致框架,具体仍然说不系统。
反问
然后就是反问,我问了实习那边任务是应用还是偏科研,然后面试官说不会以发论文为目的,是算法在具体任务重应用和优化,然后就结束了。
总结
总之这个面试是最差的一次,属于是项目没啥东西被拷打,后面问八股我也一点没准备,直接凉透,面完直接胳膊下面全是汗,湿透了,估计是凉了,不过也是一次经验,以后要多准备一些基础知识,吃一堑长一智,加油吧。
最后补个学习的内容:算法工程师面试常考手撕题
引用链接https://mp.weixin.qq.com/s/TAFvUlqdyqP-W6C10F1Hzw
- 算法工程师面试常考手撕题
- 注意力(Attention)篇
- 手撕单头注意力机制(ScaledDotProductAttention)函数
- 手撕多头注意力(MultiHeadAttention)
- 手撕自注意力机制函数(SelfAttention)
- GPT2 解码中的KV Cache
- 手撕 MQA 算法
- 基础机器学习算法篇
- 手撕 numpy写线性回归的随机梯度下降(stochastic gradient descent,SGD)
- 手撕 k-means 算法
- 手撕 Layer Normalization 算法
- 手撕 Batch Normalization 算法
- 解码算法篇
- 手撕 贪心搜索 (greedy search)
- 手撕 集束搜索 beamsearch 算法
- 手撕 温度参数采样(Temperature Sampling)算法
- 手撕 Top-K Sampling算法
- 手撕 Top-P (Nucleus) Sampling 算法
- 神经网络篇
- 手撕反向传播(backward propagation,BP)法
- 手撕 卷积神经网络(CNN)法
- 手撕 循环神经网络(RNN)法
- 手撕 LSTM法
- 手撕 二维卷积 算法
- 位置编码篇
- 手撕 绝对位置编码 算法
- 手撕 可学习位置编码 算法
- 手撕 相对位置编码 算法
- 手撕 rope 算法
- 面试题汇总
- 致谢
- 注意力(Attention)篇
注意力(Attention)篇
手撕单头注意力机制(ScaledDotProductAttention)函数
输入是query和 key-value,注意力机制首先计算query与每个key的关联性(compatibility),每个关联性作为每个value的权重(weight),各个权重与value的乘积相加得到输出。
1 | class ScaledDotProductAttention(nn.Module): |
手撕多头注意力(MultiHeadAttention)
1 | class MultiHeadAttention(nn.Module): |
手撕自注意力机制函数(SelfAttention)
Self-Attention。和Attention类似,他们都是一种注意力机制。不同的是Attention是source对target,输入的source和输出的target内容不同。例如英译中,输入英文,输出中文。而Self-Attention是source对source,是source内部元素之间或者target内部元素之间发生的Attention机制,也可以理解为Target=Source这种特殊情况下的注意力机制。
1 | class SelfAttention(nn.Module): |
GPT2 解码中的KV Cache
无论是encoder-decoder结构,还是现在我们最接近AGI的decoder-only的LLM,解码生成时都是自回归auto-regressive的方式。
也就是,解码的时候,先根据当前输入 ,生成下一个 ,然后把新生成的 拼接在 后面,获得新的输入 ,再用 生成 ,依此迭代,直到生成结束。
我们可以注意到,下一个step的输入其实包含了上一个step的内容,而且只在最后面多了一点点(一个token)。那么下一个step的计算应该也包含了上一个step的计算。
但是模型在推理的时候可不管这些,无论你是不是只要最后一个字的输出,它都把所有输入计算一遍,给出所有输出结果。
也就是说中间有很多我们用不到的计算,这样就造成了浪费。
而且随着生成的结果越来越多,输入的长度也越来越长,上面这个例子里,输入长度就从step0的10个,每步增长1,直到step5的15个。如果输入的instruction是让模型写作文,那可能就有800个step。这个情况下,step0被算了800次,step1被算了799次…这样浪费的计算资源确实不容忽视。
有没有什么办法可以重复利用上一个step里已经计算过的结果,减少浪费呢?
答案就是KV Cache,利用一个缓存,把需要重复利用的中间计算结果存下来,减少重复计算。
而 k 和 v 就是我要缓存的对象。
想象一下,在上面的例子中,假设我们一开始的输入就是3个字,我们第一次预测就是预测第4个字,那么由于一开始没有任何缓存,所有我们每一层还是要老实地计算一遍。然后把 k 、 v 值缓存起来。
则有
kv cache的下标l表示模型层数。
在进行第二次预测,也就是预测第5个字的时候,在第l层的时候,由于前面我们缓存了每层的ku 值,那本层就只需要算新的 o3,而不用算 o0、o1、o2。
因为第l层的 o0、o1、o2本来会经过FNN层之后进到 l十1 层,再经过新的投影变换,成为 l + 1 层的 k、υ 值,但是l十 1 层的 k、υ值我们已经缓存过了!
然后我们把本次新增算出来的 k、υ 值也存入缓存。
这样就节省了attention和FFN的很多重复计算。
transformers中,生成的时候传入use_cache=True就会开启KV Cache。
也可以简单看下GPT2中的实现,中文注释的部分就是使用缓存结果和更新缓存结果
1 | Class GPT2Attention(nn.Module): |
总的来说,KV Cache是以空间换时间的做法,通过使用快速的缓存存取,减少了重复计算。(注意,只有decoder结构的模型可用,因为有mask attention的存在,使得前面的token可以不用关注后面的token)
手撕 MQA 算法
MQA 让所有的头之间 共享 同一份 Key 和 Value 矩阵,每个头只单独保留了一份 Query 参数,从而大大减少 Key 和 Value 矩阵的参数量。
1 | class MultiQueryAttention(nn.Module): |
基础机器学习算法篇
手撕 numpy写线性回归的随机梯度下降(stochastic gradient descent,SGD)
在每次更新时用1个样本,可以看到多了随机两个字,随机也就是说我们用样本中的一个例子来近似我所有的样本,来调整θ,因而随机梯度下降是会带来一定的问题,因为计算得到的并不是准确的一个梯度,对于最优化问题,凸问题,虽然不是每次迭代得到的损失函数都向着全局最优方向, 但是大的整体的方向是向全局最优解的,最终的结果往往是在全局最优解附近。
1 | # 数据加载 |
手撕 k-means 算法
1 | import numpy as np |
手撕 Layer Normalization 算法
1 | import torch |
手撕 Batch Normalization 算法
1 | class MyBN: |
解码算法篇
手撕 贪心搜索 (greedy search)
贪心搜索(greedy search)在每个时间步 t 都选取当前概率分布中概率最大的词,即
直到 yt 为或达到预设最大长度时停止生成。
贪心搜索本质上是局部最优策略,但并不能保证最终结果一定是全局最优的。由于贪心搜索在解码的任意时刻只保留一条候选序列,所以在搜索效率上,贪心搜索的复杂度显著低于穷举搜索。
1 | def greedy_decoding(input_ids, max_tokens=300): |
手撕 集束搜索 beamsearch 算法
在NLP翻译或对话任务中,在句子解码阶段,经常用到一种搜索算法beam search。这个算法有时候在大厂面试中,甚至可能会被要求手写实现。这里就从beam search的原理出发,最后手写实现一个beam search。
- 思路:beam search在贪心搜索上进一步扩大了搜索范围,贪心搜索每下一步只考虑当前最优的top-1结果,beam search考虑最优的top-k个结果。
1 | import torch |
手撕 温度参数采样(Temperature Sampling)算法
温度参数采样(Temperature Sampling)常用于基于概率的生成模型,如语言模型。它通过引入一个称为“温度”(Temperature)的参数来调整模型输出的概率分布,从而控制生成文本的多样性。
在温度参数采样中,模型在每个时间步生成词语时,会计算出词语的条件概率分布。然后模型将这个条件概率分布中的每个词语的概率值除以温度参数,对结果进行归一化处理,获得新的归一化概率分布。较高的温度值会使概率分布更平滑,从而增加生成文本的多样性。低概率的词语也有较高的可能性被选择;而较低的温度值则会使概率分布更集中,更倾向于选择高概率的词语,因此生成的文本更加确定性。最后模型根据这个新的归一化概率分布进行随机采样,选择生成的词语。
1 | import torch |
手撕 Top-K Sampling算法
Top-K 采样(在每个时间步选择条件概率排名前 K 的词语,然后在这 K 个词语中进行随机采样。这种方法既能保持一定的生成质量,又能增加文本的多样性,并且可以通过限制候选词语的数量来控制生成文本的多样性。
这个过程使得生成的文本在保持一定的生成质量的同时,也具有一定的多样性,因为在候选词语中仍然存在一定的竞争性。
1 | def top_k_sampling(input_ids, max_tokens=100, top_k=50, temperature=1.0): |
手撕 Top-P (Nucleus) Sampling 算法
Nucleus Sampling(核采样),也被称为Top-p Sampling旨在在保持生成文本质量的同时增加多样性。这种方法可以视作是Top-K Sampling的一种变体,它在每个时间步根据模型输出的概率分布选择概率累积超过给定阈值p的词语集合,然后在这个词语集合中进行随机采样。这种方法会动态调整候选词语的数量,以保持一定的文本多样性。
在Nucleus Sampling中,模型在每个时间步生成词语时,首先按照概率从高到低对词汇表中的所有词语进行排序,然后模型计算累积概率,并找到累积概率超过给定阈值p的最小词语子集,这个子集就是所谓的“核”(nucleus)。模型在这个核中进行随机采样,根据词语的概率分布来选择最终输出的词语。这样做可以保证所选词语的总概率超过了阈值p,同时也保持了一定的多样性。
参数p是Nucleus Sampling中的重要参数,它决定了所选词语的概率总和。p的值会被设置在(0,1]之间,表示词语总概率的一个下界。
Nucleus Sampling 能够保持一定的生成质量,因为它在一定程度上考虑了概率分布。通过选择概率总和超过给定阈值p的词语子集进行随机采样,Nucleus Sampling 能够增加生成文本的多样性。
1 | def top_p_sampling(input_ids, max_tokens=100, top_p=0.95): |
神经网络篇
手撕反向传播(backward propagation,BP)法
BP算法就是反向传播,要输入的数据经过一个前向传播会得到一个输出,但是由于权重的原因,所以其输出会和你想要的输出有差距,这个时候就需要进行反向传播,利用梯度下降,对所有的权重进行更新,这样的话在进行前向传播就会发现其输出和你想要的输出越来越接近了。
1 | # 生成权重以及偏执项layers_dim代表每层的神经元个数, |
手撕 卷积神经网络(CNN)法
1 | import torch |
手撕 循环神经网络(RNN)法
1 | # encoding:utf-8 |
手撕 LSTM法
1 | # -*- coding:UTF-8 -*- |
手撕 二维卷积 算法
1 | import numpy as np |
位置编码篇
手撕 绝对位置编码 算法
1 | class SinPositionEncoding(nn.Module): |
手撕 可学习位置编码 算法
1 | class TrainablePositionEncoding(nn.Module): |
手撕 相对位置编码 算法
1 | class RelativePosition(nn.Module): |
手撕 rope 算法
1 | import torch |
print(res.shape, att_scores.shape)
面试题汇总
- 大模型微调的经验与感想分享
- 百度-NLP算法工程师面经
- 美团-大模型算法工程师面经
- 小米-NLP算法工程师面试题
- 好未来-NLP算法工程师面经
- 百度大模型算法工程师面经
- 昆仑天工大模型算法工程师
- 阿里大模型算法工程师一面
- 算法工程师面试常考手撕题
- 搜狐大模型算法工程师
- 字节大模型算法实习生
- 理想汽车大模型算法实习生
- 百度大模型算法实习生面试题
- 腾讯大模型算法实习生面试题
- 阿里大模型算法工程师一面
- 某大厂大模型算法工程师面试题
- 说说百度大模型算法工程师二面经历
- 阿里大模型算法工程师面试小结
致谢
- LLMs 千面郎君 更新版 https://mp.weixin.qq.com/s/C6NdO_Ebj3DQx2AVAAgQRQ
- LLMs九层妖塔 https://mp.weixin.qq.com/s/Eh0tY1zx2FqXQqIGa2dIBA
- NLP 面无不过 https://github.com/km1994/NLP-Interview-Notes
来自: 算法工程师面试常考手撕题(更新)