面试会问到的AI 算法题(二)
1. 常见的损失函数
常见的损失函数分为回归和分类两类:
-
回归问题:
- 均方误差(MSE, Mean Squared Error): 衡量预测值与真实值的平方误差,常用于回归任务。
- 平均绝对误差(MAE, Mean Absolute Error): 衡量预测值与真实值之间的绝对误差,抗噪性强。
-
分类问题:
- 交叉熵损失(Cross Entropy Loss): 用于分类任务,尤其是多分类任务,衡量真实标签与预测概率之间的差异。
- 二元交叉熵(Binary Cross Entropy): 特别适用于二分类任务。
- KL 散度(KL Divergence): 衡量两个概率分布的差异,常用于模型蒸馏或对抗训练。
2. GBDT 和 Adaboost 的区别
- GBDT(Gradient Boosting Decision Tree): 通过梯度下降法不断优化模型,每棵新树是对前一棵树的误差进行拟合。GBDT 可以处理回归和分类任务,且能使用自定义损失函数。
- Adaboost: 通过分配权重给样本,逐步提升模型对难以分类样本的关注度。每棵树基于前一棵树的错误进行调整,重点是更新样本权重,而非梯度信息。
主要区别:
- 损失优化方式: GBDT 通过梯度下降优化损失,Adaboost 通过更新样本权重调整模型。
- 模型类型: GBDT 通常使用决策树作为基学习器,Adaboost 可以使用其他弱学习器。
- 应用领域: GBDT 更灵活,可以处理回归任务,而 Adaboost 主要用于分类。
3. 神经网络的优化器
常见的优化器有:
- SGD(Stochastic Gradient Descent): 随机梯度下降,简单高效。
- Momentum: 在SGD上引入动量项,缓解振荡,提高收敛速度。
- Adam: 结合动量和RMSProp的优势,使用自适应学习率,广泛应用。
- RMSProp: 根据最近一段时间的梯度平方进行自适应学习率调整,适合处理非平稳目标。
- Adagrad: 对学习率进行自适应调整,适合稀疏数据。
4. 神经网络的初始化方法
- 随机初始化: 将权重初始化为小随机值,避免对称性。
- Xavier 初始化: 根据输入和输出层的节点数进行初始化,适用于Sigmoid和tanh激活函数。
- He 初始化: Xavier的改进版,适合ReLU等激活函数。
- 零初始化: 常用于偏置,不适合权重,因为会导致对称性问题。
5. DETR 中匈牙利匹配算法的具体流程
DETR(Detection Transformer)使用匈牙利算法进行一对一目标检测匹配,具体流程:
- 计算代价矩阵: 根据预测的边界框和真实边界框的L1损失与分类损失构建代价矩阵。
- 匈牙利算法: 使用匈牙利算法解决二分图的最优匹配问题,找到每个预测框与真实框的最优匹配对,确保一一对应关系。
- 反向传播: 基于匹配结果计算损失并更新模型参数。
6. 为什么 Transformers 要用 LayerNorm?
**LayerNorm(层归一化)**的主要作用是稳定训练过程,尤其是在深度神经网络中,通过归一化输入的激活值减少梯度消失和梯度爆炸问题,从而加速收敛,适应不同的输入分布。与 BatchNorm 不同,LayerNorm 针对每个样本的特征进行归一化,不依赖于批量数据。
7. 为什么 self-attention 可以堆叠多层,有什么作用?
Self-attention 可以堆叠多层是因为每一层能够捕捉不同层次的特征:
- 局部特征和全局特征结合: 通过堆叠,较低层捕捉局部特征,较高层捕捉全局特征,形成逐渐复杂的特征表示。
- 长程依赖建模: 堆叠多层使得模型能够捕捉序列中的长程依赖关系,适用于自然语言处理等任务。
8. 介绍常用的聚类算法
- KMeans: 基于距离的迭代算法,选择
K
个初始聚类中心,然后根据样本到中心的距离分配类标签,最终优化类内的方差。 - DBSCAN: 基于密度的聚类算法,通过寻找密度相似的点形成聚类,适合处理有噪声的数据,能够发现不同形状的簇。
- Mean Shift: 基于平滑核密度估计的聚类算法,计算局部密度最大值并不断移动均值,适合发现任意形状和数量的簇。
计算机基础知识和编程题
1. 链表反转
链表反转是一种常见的面试题,具体的实现思路如下:
struct ListNode {
int val;
ListNode* next;
ListNode(int x) : val(x), next(NULL) {}
};
ListNode* reverseList(ListNode* head) {
ListNode* prev = NULL;
ListNode* curr = head;
while (curr) {
ListNode* nextTemp = curr->next;
curr->next = prev;
prev = curr;
curr = nextTemp;
}
return prev;
}
2. 回调函数怎么实现?
回调函数是一种将函数作为参数传递并在合适时机调用的方式。以下是 C++ 中的回调函数示例:
#include <iostream>
using namespace std;
void myCallback(int result) {
cout << "The result is: " << result << endl;
}
void performOperation(int a, int b, void(*callback)(int)) {
int sum = a + b;
callback(sum); // 回调函数
}
int main() {
performOperation(5, 10, myCallback);
return 0;
}
3. Linux 的指令
常用的 Linux 指令有:
- 文件操作:
ls
(列出目录),cp
(复制文件),mv
(移动/重命名文件),rm
(删除文件)。 - 权限管理:
chmod
(更改权限),chown
(更改文件所有者)。 - 进程管理:
ps
(查看进程),kill
(杀死进程),top
(实时查看进程信息)。 - 网络命令:
ping
(测试网络连接),netstat
(显示网络连接)。
4. 寻找一次曲线(离散的点连成的)的最小值
可以使用抛物线插值法或线性搜索法找到离散点构成的曲线的最小值。简单的搜索方法如下:
def find_minimum(points):
min_point = points[0]
for point in points:
if point[1] < min_point[1]:
min_point = point
return min_point
# 输入离散的点 (x, y)
points = [(1, 3), (2, 1), (3, 4), (4, 2)]
minimum = find_minimum(points)
print(f"The minimum point is: {minimum}")
该算法通过遍历所有点,比较 y
值找到最小值点。