写在前面
【三年面试五年模拟】旨在整理&挖掘AI算法工程师在实习/校招/社招时所需的干货知识点与面试经验,力求让读者在获得心仪offer的同时,增强技术基本面。
欢迎大家关注Rocky的公众号:WeThinkIn
欢迎大家关注Rocky的知乎:Rocky Ding
AIGC算法工程师面试面经秘籍分享:WeThinkIn/Interview-for-Algorithm-Engineer欢迎大家Star~
获取更多AI行业的前沿资讯与干货资源
WeThinkIn最新福利放送:大家只需关注WeThinkIn公众号,后台回复“简历资源”,即可获取包含Rocky独家简历模版在内的60套精选的简历模板资源,希望能给大家在AIGC时代带来帮助。
Rocky最新发布Stable Diffusion 3和FLUX.1系列模型的深入浅出全维度解析文章,点击链接直达干货知识:https://zhuanlan.zhihu.com/p/684068402
大家好,我是Rocky。
又到了定期阅读《三年面试五年模拟》文章的时候了!本周期共更新了60多个AIGC面试高频问答,依旧干货满满!诚意满满!
《三年面试五年模拟》系列文章帮助很多读者获得了心仪的算法岗offer,收到了大家的很多好评,Rocky觉得很开心也很有意义。
在AIGC时代到来后,Rocky对《三年面试五年模拟》整体战略方向进行了重大的优化重构,在秉持着Rocky创办《三年面试五年模拟》项目初心的同时,增加了AIGC时代核心的版块栏目,详细的版本更新内容如下所示:
- 整体架构:分为AIGC知识板块和AI通用知识板块。
- AIGC知识板块:分为AI绘画、AI视频、大模型、AI多模态、数字人这五大AIGC核心方向。
- AI通用知识板块:包含AIGC、传统深度学习、自动驾驶等所有AI核心方向共通的知识点。
Rocky已经将《三年面试五年模拟》项目的完整版构建在Github上:https://github.com/WeThinkIn/Interview-for-Algorithm-Engineer/tree/main,本周期更新的60+AIGC面试高频问答已经全部同步到项目中了,欢迎大家star!
本文是《三年面试五年模拟》项目的第二十五式,考虑到易读性与文章篇幅,Rocky本次只从Github完整版项目中摘选了2024年10月21号-2024年11月3号更新的部分高频&干货面试知识点和面试问题,并配以相应的参考答案(精简版),供大家学习探讨。
在《三年面试五年模拟》版本更新白皮书,迎接AIGC时代中我们阐述了《三年面试五年模拟》项目在AIGC时代的愿景与规划,也包含了项目共建计划,感兴趣的朋友可以一起参与本项目的共建!
《三年面试五年模拟》系列将陪伴大家度过整个AI行业的职业生涯,并且让大家能够持续获益。
So,enjoy(与本文的BGM一起食用更佳哦):
正文开始
目录先行
AI绘画基础:
-
Midjourney迭代至今有哪些优秀的特点?
-
Midjourney有哪些关键的参数?
深度学习基础:
-
什么是NewGELU激活函数?
-
CNN+Transformer组合的架构有哪些优势?
机器学习基础:
-
机器学习中将余弦相似度作为损失函数有哪些优势?
-
介绍一下机器学习中的L2损失函数
Python编程基础:
-
介绍一下Python中的继承(Inheritance)思想
-
介绍一下Python中的多态(Polymorphism)思想
模型部署基础:
-
如何将ONNX模型从GPU切换到CPU中进行缓存?
-
介绍一下Base64编码图像的原理
计算机基础:
-
介绍一下Gunicorn的原理,并举例其在AI行业的作用
-
介绍一下Uvicorn的原理,并举例其在AI行业的作用
开放性问题:
-
AI算法工程师从纯技术到技术管理的差异是什么?
-
AI公司的各个部门如何减小信息传递的损耗率?
AI绘画基础
【一】Midjourney迭代至今有哪些优秀的特点?
Rocky认为Midjourney系列是AIGC时代AI绘画ToC产品的一个非常有价值的标杆,我们需要持续研究挖掘其价值与优秀特点:
- 图像生成整体性能持续提升。
- 图像细节持续提升,包括图像背景、内容层次、整体光影、人物结构、手部特征、皮肤质感、整体构图等。
- 语义理解持续增强,生成的图像内容与输入提示词更加一致。
- 审美标准持续提升。
- 更多辅助功能支持:超分、可控生成、人物一致性、风格参考等。
- 用户易用性持续提升:用户输入更加简洁的提示词就能生成高质量的图片,更加符合用户的使用习惯。
【二】Midjourney有哪些关键的参数?
Rocky认为,了解Midjourney的关键参数,能够从中挖掘出一些借鉴价值,并对其底层技术进行判断,是非常有价值的事情。
Rocky也会持续补充更新Midjourney的最新关键参数。
1. 版本参数:--version
或 --v
作用:
指定使用 Midjourney 的模型版本。不同版本的模型在风格、细节和渲染效果上有所区别。
使用方法:
--version <版本号>
或
--v <版本号>
示例:
/imagine prompt: a serene landscape --v 6
2. 风格化参数:--stylize
或 --s
作用:
控制生成图像的艺术风格程度。数值越大,图像越具艺术性;数值越小,图像越接近于严格按照提示生成。可以用数值范围是0-1000,默认值是100。
默认情况下,Midjourney会为图像加上100的–s参数数值。如果将数值调到低于100,那么画面的细节会变少、质感会更粗糙,图像整体质量会下降;而如果将数值调至高于100,那么画面的细节会更丰富、光影、纹理的质感也会更精致。如下图,随着–s数值的提升,树精灵的服装变得更华丽了,面部五官也更加可爱精致,与–s为0时有明显的区别。
使用方法:
--stylize <数值>
或
--s <数值>
示例:
/imagine prompt: a portrait of a cat --s 1000
3. 宽高比参数:--aspect
或 --ar
作用:
指定生成图像的宽高比例。
=使用方法:
--aspect <宽比>:<高比>
或
--ar <宽比>:<高比>
示例:
/imagine prompt: a tall skyscraper --ar 9:16
详细说明:
- 常用比例:
1:1
(正方形)16:9
(宽屏)9:16
(竖屏)- 自定义比例,如
4:3
、3:2
等。
- **影响:**调整图像的构图和布局,以适应特定的显示需求,如手机壁纸、海报等。
4. 质量参数:--quality
或 --q
作用:
控制图像生成的质量和渲染速度。较高的质量会产生更精细的图像,但需要更多的时间和资源。
使用方法:
--quality <数值>
或
--q <数值>
示例:
/imagine prompt: an intricate mechanical watch --q 2
详细说明:
- 数值选项:
0.25
(低质量,速度快)0.5
(中等质量)1
(默认质量)2
(高质量,速度慢)
- **影响:**提高质量参数会增加图像的细节和分辨率,但渲染时间也会相应增加。适用于对细节有高要求的图像生成。
5. 种子参数:--seed
作用:
指定随机数生成的种子,以控制图像生成的随机性。使用相同的种子和提示,可以复现相似的图像。
使用方法:
--seed <数值>
示例:
/imagine prompt: a mystical forest --seed 123456789
详细说明:
- **数值范围:**0 到 4294967295 之间的整数。
- 影响:
- **复现性:**相同的提示和种子会生成相似的图像,方便对结果进行微调和比较。
- **多样性:**更改种子值可以探索不同的图像变体。
6. 混乱度参数:--chaos
作用:
Chaos 是一种混沌值参数,可以缩写为 --c 添加在提示词之后,控制生成图像的随机性和不可预测性。较高的值会产生更意想不到的结果。可以用数值范围是0-100,默认值是0。
Midjourney对每组提示词返回的并非单张图像,而是4张,这让我们一次就能得到多张图像,提升了出图效率。在之前的版本中,每次生成的4张图像是非常相似的,官方觉得这不利于用户获取更多样的结果,于是在V6版本中调大了图像间的差异性,让4张图像在风格、构图、内容等方面有明显不同。
如下图,–c 数值达到 25 时,画面虽然还能保持 “穿白色衣服,头戴桃子花环的男孩” 这一形象,但已经不再局限于 “3D、玩偶” 的风格范围了,拓展到真人、布偶、陶偶等类型上;而在数值达到 50 以及更高时,画面已经和最初的提示词关联度很低了,风格和内容都变得很随机。
使用方法:
--chaos <数值>
示例:
/imagine prompt: abstract shapes and colors --chaos 80
详细说明:
- **数值范围:**0 到 100。
- **影响:**增加混乱度会使生成的图像更具创意和不可预测性,但可能与提示的相关性降低。
7. 图像提示参数:--image
作用:
提供一个参考图像,指导生成的图像风格或内容。本质上和Stable Diffusion系列的图生图功能是一样的。
使用方法:
在提示中上传图像或提供图像 URL
示例:
/imagine prompt: [上传的图片] + a sunset over the ocean
详细说明:
- **使用方法:**在提示中添加一张图片,Midjourney 将其作为参考。
- **影响:**生成的图像会结合文字描述和参考图像的风格或内容。
8. 负面提示参数:--no
作用:
排除特定元素或特征,使生成的图像不包含指定内容。与Stable Diffusion系列的Negative Prompt效果一致。
使用方法:
--no <不希望出现的元素>
示例:
/imagine prompt: a city street at night --no cars
详细说明:
- **影响:**指导模型避免生成包含指定元素的内容,提高结果的符合度。
9. Tile 参数:--tile
作用:
生成可无缝平铺的图像,适用于纹理和背景设计。
使用方法:
--tile
示例:
/imagine prompt: a floral pattern --tile
详细说明:
- **影响:**生成的图像可以在水平和垂直方向上无缝衔接,适合用于壁纸、纹理等设计。
10. UPBETA 参数:--UPBETA
作用:
提供更好的图像质量和细节,在图像的细节处理上有更好的表现,呈现出更精细的纹理和轮廓。与Stable Diffusion系列模型的精绘功能非常相似。
使用用法:
/imagine prompt: <描述文本> --upbeta
示例:
/imagine prompt: a futuristic city skyline at sunset --upbeta
深度学习基础
【一】什么是NewGELU激活函数?
NewGELU 是对传统 GELU (Gaussian Error Linear Unit) 的一种改进。GELU 本身在许多AI模型中表现优异(如 Transformer 系列模型),而 NewGELU 在保留 GELU 平滑特性的同时,进一步优化了计算效率和非线性特性,从而可以在一些AI任务中获得更好的表现。
一、GELU 激活函数的回顾
在了解 NewGELU 之前,我们先回顾一下 GELU 激活函数的定义和特点,以便更好地理解 NewGELU 的改进之处。
1. GELU 的数学定义
GELU 激活函数的数学表达式为:
GELU ( x ) = x ⋅ Φ ( x ) \text{GELU}(x) = x \cdot \Phi(x) GELU(x)=x⋅Φ(x)
其中, Φ ( x ) \Phi(x) Φ(x) 是标准正态分布的累积分布函数(CDF),定义为:
Φ ( x ) = 1 2 ( 1 + erf ( x 2 ) ) \Phi(x) = \frac{1}{2} \left(1 + \text{erf}\left(\frac{x}{\sqrt{2}}\right)\right) Φ(x)=21(1+erf(2 x))
由于累积分布函数的计算较为复杂,GELU 常使用以下近似表达式来加速计算:
GELU ( x ) ≈ 0.5 ⋅ x ⋅ ( 1 + tanh ( 2 π ( x + 0.044715 ⋅ x 3 ) ) ) \text{GELU}(x) \approx 0.5 \cdot x \cdot \left(1 + \tanh\left(\sqrt{\frac{2}{\pi}} \left( x + 0.044715 \cdot x^3 \right)\right)\right) GELU(x)≈0.5⋅x⋅(1+tanh(π2 (x+0.044715⋅x3)))
2. GELU 的特点
- 平滑性:GELU 是连续可导的函数,使得梯度流动更加顺畅。
- 概率性:GELU 基于输入值的大小概率性地保留或抑制输入,从而实现了平滑的门控效果。
- 性能:在许多AI模型中,如 BERT、GPT 等,GELU 显著优于 ReLU、Tanh 等传统激活函数。
二、NewGELU 的引入
NewGELU 是一种对 GELU 的改进,其目标是:
- 优化计算效率:通过更简洁的公式减少计算量。
- 改善模型性能:在保持 GELU 平滑特性的同时,进一步提升深度学习模型的表现。
三、NewGELU 激活函数的定义
1. 数学表达式
NewGELU 激活函数的近似表达式为:
NewGELU ( x ) = 0.5 ⋅ x ⋅ ( 1 + tanh ( 2 π ⋅ ( x + 0.0356774 ⋅ x 3 ) ) ) \text{NewGELU}(x) = 0.5 \cdot x \cdot \left(1 + \tanh\left(\sqrt{\frac{2}{\pi}} \cdot (x + 0.0356774 \cdot x^3)\right)\right) NewGELU(x)=0.5⋅x⋅(1+tanh(π2 ⋅(x+0.0356774⋅x3)))
与 GELU 的近似表达式对比:
GELU ( x ) ≈ 0.5 ⋅ x ⋅ ( 1 + tanh ( 2 π ( x + 0.044715 ⋅ x 3 ) ) ) \text{GELU}(x) \approx 0.5 \cdot x \cdot \left(1 + \tanh\left(\sqrt{\frac{2}{\pi}} \left( x + 0.044715 \cdot x^3 \right)\right)\right) GELU(x)≈0.5⋅x⋅(1+tanh(π2 (x+0.044715⋅x3)))
2. 公式的简化
NewGELU 的公式与 GELU 非常相似,但将常数 0.044715
改为 0.0356774
。这一小小的改动,使得 NewGELU 在计算上更加高效,且在某些任务中表现略优于标准 GELU。
四、NewGELU 的特性
1. 更高的计算效率
- NewGELU 通过调整公式中的系数,减少了计算复杂度,特别是在模型推理时表现出色。
- 虽然调整系数的幅度很小,但这对计算量较大的深度学习模型来说可以带来实际的性能提升。
2. 平滑的非线性
- 与 GELU 一样,NewGELU 也是连续可导的,并且具有平滑的曲线。这样的非线性特性对深层网络中的梯度流动非常友好。
- 负值区域:在负值区域,NewGELU 的输出逐渐接近于零,但并不会像 ReLU 那样直接截断为零,因此可以保留一部分负值信息。
3. 自适应性
- NewGELU 的自适应性体现在它对不同大小的输入值可以进行“自门控”。大输入值的激活值接近于输入值,而小输入值的激活值则接近于零。
- 这种特性类似于“概率门控”,能够在保持输入特征完整性的同时,抑制噪声和无关信息。
五、总结
- NewGELU 是对 GELU 激活函数的改进,通过简化公式并优化常数项,使得计算效率更高。
- 特点:具有平滑过渡、负值信息保留、自门控等特性,适用于各种深度学习模型。
- 应用场景:Transformer、CNN、强化学习等任务中,NewGELU 提供了更好的梯度流动和模型收敛性能。
- 实验结果:在 NLP 和图像任务中,新型模型往往采用 NewGELU,以提升模型的训练速度和准确率。
【二】CNN+Transformer组合的架构有哪些优势?
在AI行业中,CNN(卷积神经网络)和Transformer结合的架构将 CNN 的局部特征提取能力和 Transformer 的全局特征捕获能力相结合,具备多个显著优势。
1. 局部和全局特征的有效结合
- CNN 提取局部特征:CNN 通过卷积操作和池化层,擅长从图像中提取局部的空间特征,比如边缘、纹理和形状等。这种局部特征对于视觉任务非常重要,因为它们包含了物体的基本形状信息。
- Transformer 捕捉全局依赖关系:Transformer 通过自注意力机制,可以在全局范围内建模任意两个位置之间的依赖关系,因此在捕捉全局上下文信息方面非常强大。
- 结合的优势:CNN 和 Transformer 的组合能够在捕捉细粒度的局部特征(由 CNN 负责)和理解高层次的全局关系(由 Transformer 负责)之间找到平衡,这使得模型在处理复杂任务时更具优势。
2. 计算效率和模型性能的平衡
- CNN 的计算效率高:在处理大尺寸输入(如高分辨率图像)时,CNN 可以通过局部感受野高效地进行计算,因此计算成本较低。
- Transformer 自注意力机制的灵活性:Transformer 可以通过自注意力机制对图像的不同区域进行关注,特别是对图像中的重要部分施加更多的权重。
- 结合的优势:在组合架构中,CNN 负责初步提取特征,并将其输入到 Transformer 中以进一步处理。这样可以减少 Transformer 直接处理高维输入的计算负担,提高整体计算效率。
3. 提高模型的鲁棒性和泛化能力
- CNN 在捕捉空间特征方面具有鲁棒性:CNN 的卷积操作具有平移不变性(translation invariance),可以对图像的平移、缩放等进行一定程度的适应,因此模型在处理不同视角或位置的物体时具备一定的鲁棒性。
- Transformer 的全局感知能力增强了泛化能力:Transformer 的全局注意力机制使得模型能够在特征空间上建立更广泛的联系,增强了模型在复杂场景中的泛化能力。
- 结合的优势:CNN+Transformer 的组合可以让模型在数据变化较大或数据分布复杂的情况下,依然能够捕获重要特征并保持良好的表现。实验表明,这种组合模型在处理高复杂度数据集(如 ImageNet)时,通常优于纯 CNN 模型或纯 Transformer 模型。
4. 多模态任务中的优势
- CNN 和 Transformer 都能处理多模态输入:CNN 擅长图像特征提取,而 Transformer 已被广泛用于文本、语音和图像的序列化处理,因此这种组合架构在多模态任务中具有显著优势。
- 结合的优势:在多模态任务(如图像-文本匹配、视觉问答)中,CNN 可以处理图像特征,Transformer 可以同时处理图像和文本信息。这种架构可以轻松处理异构数据,将不同模态的数据融合在一起,以实现跨模态理解和生成。
机器学习基础
【一】机器学习中将余弦相似度作为损失函数有哪些优势?
在机器学习中,余弦相似度(Cosine Similarity)是一种用于衡量两个向量之间相似度的常用方法,尤其适用于高维空间的特征向量。将余弦相似度作为损失函数(通常转化为余弦相似度损失)具有多个优势,特别是在文本、图像特征和推荐系统等任务中。
1. 余弦相似度的定义与计算
余弦相似度的公式如下:
Cosine Similarity = cos ( θ ) = A ⃗ ⋅ B ⃗ ∣ ∣ A ⃗ ∣ ∣ ⋅ ∣ ∣ B ⃗ ∣ ∣ \text{Cosine Similarity} = \cos(\theta) = \frac{\vec{A} \cdot \vec{B}}{||\vec{A}|| \cdot ||\vec{B}||} Cosine Similarity=cos(θ)=∣∣A ∣∣⋅∣∣B ∣∣A ⋅B
其中:
- A ⃗ \vec{A} A 和 B ⃗ \vec{B} B 是两个向量。
- 分子部分 A ⃗ ⋅ B ⃗ \vec{A} \cdot \vec{B} A ⋅B 是两个向量的内积。
- 分母部分 ∣ ∣ A ⃗ ∣ ∣ ⋅ ∣ ∣ B ⃗ ∣ ∣ ||\vec{A}|| \cdot ||\vec{B}|| ∣∣A ∣∣⋅∣∣B ∣∣ 是两个向量的模长的乘积。
余弦相似度的取值范围为 ([-1, 1]):
- 1 表示两个向量方向完全一致(最相似)。
- 0 表示两个向量正交(无相关性)。
- -1 表示两个向量方向完全相反(最不相似)。
余弦相似度损失通常是将余弦相似度取负值,或使用 (1 - \text{Cosine Similarity}),使得两个向量越相似,损失越小。
2. 余弦相似度作为损失函数的优势
2.1 忽略向量的模长,专注于方向
- 余弦相似度仅衡量两个向量的方向相似性,而不考虑它们的模长。
- 在许多任务中,如文本表示或图像嵌入,两个向量可能在数值上有较大的差异(模长不同),但如果方向相同,则可以认为它们表示相似的概念。
- 这种性质在嵌入空间中尤其有用,因为我们通常关心特征的相对相似性,而不是绝对的数值大小。
2.2 适用于高维稀疏数据
- 余弦相似度在高维稀疏数据(如文本向量、用户行为数据)中表现良好,因为它只计算非零元素的方向相似度。
- 在推荐系统中,用户的特征向量可能是高维且稀疏的(如用户对物品的评分),余弦相似度可以有效处理这种稀疏数据,避免因为缺失值影响相似度计算。
2.3 对特征进行归一化,有助于稳定训练
- 余弦相似度实际上是将向量归一化到单位球面上,确保向量长度一致。
- 这种归一化操作可以减少模型对数值缩放变化的敏感性,使得模型更加鲁棒,特别是在特征值变化较大的情况下。
2.4 更符合距离度量的直观意义
- 在很多任务中,我们希望将相似的对象拉近、不相似的对象拉远。使用余弦相似度作为损失函数,可以很好地表达这个需求。
- 在对比学习(contrastive learning)中,余弦相似度损失可以有效地将相似对象的嵌入向量方向拉得更近,将不相似对象的方向分开。
3. 余弦相似度损失的实际应用场景
3.1 文本相似度任务
在自然语言处理(NLP)中,余弦相似度广泛用于衡量文本相似度,如句子嵌入的比较。在这种场景中:
- 文本经过编码器(如 BERT)得到句子嵌入向量,余弦相似度损失确保相似文本的向量方向一致。
- 余弦相似度损失可以忽略句子表示的绝对大小,只关心相对方向,有助于更好地度量语义相似性。
3.2 图像特征匹配
在图像处理任务(如人脸识别、图像检索)中,余弦相似度损失用于比较不同图像的嵌入表示:
- 将人脸图像编码成嵌入向量,余弦相似度损失可以确保相同人脸的向量方向一致,不同人脸的方向不一致。
- 使用余弦相似度损失可以增强模型对图像中相同特征的识别能力,提升匹配效果。
3.3 推荐系统中的用户与物品匹配
在推荐系统中,可以将用户与物品的特征向量转换为相同的嵌入空间:
- 使用余弦相似度损失来计算用户向量与物品向量之间的相似性,以衡量用户对物品的兴趣。
- 这种方法对高维稀疏特征尤其有效,且能适应不同用户和物品特征的变化。
总结
总的来说,我们使用余弦相似度作为损失函数的优势总结如下:
- 忽略模长,专注方向:适合不关注特征绝对大小的任务,适用于嵌入表示。
- 高效处理高维稀疏数据:特别适用于文本、推荐系统等稀疏数据。
- 归一化操作:在训练过程中更稳定,对缩放不敏感。
- 自然匹配的距离度量:直接度量相似性,更符合直观的“相似度”需求。
因此,余弦相似度损失在文本相似度、图像特征匹配、推荐系统等领域得到了广泛应用,能够有效提升模型在这些任务中的表现。
【二】介绍一下机器学习中的L2损失函数
在机器学习中,L2损失函数(也称为均方误差损失,Mean Squared Error, MSE)是一种用于评估模型预测值与真实值之间差异的常见损失函数。L2损失函数广泛应用于回归问题中,因为它具有较好的数值稳定性,且对于较大的误差给予更大的惩罚。
1. L2损失函数的定义
假设我们有一个模型,给定输入 x i x_i xi 可以输出预测值 y ^ i \hat{y}_i y^i ,并且我们知道目标值 y i y_i yi 。L2损失函数的定义如下:
L L 2 = 1 n ∑ i = 1 n ( y ^ i − y i ) 2 L_{L2} = \frac{1}{n} \sum_{i=1}^n (\hat{y}_i - y_i)^2 LL2=n1i=1∑n(y^i−yi)2
其中:
- n n n 是样本数量。
- y ^ i \hat{y}_i y^i 是模型对第 i i i 个样本的预测值。
- y i y_i yi 是第 i i i 个样本的真实值。
- ( y ^ i − y i ) 2 (\hat{y}_i - y_i)^2 (y^i−yi)2 是预测值与真实值之间误差的平方。
这个公式表示的是每个样本的预测误差平方的平均值,也被称为均方误差(Mean Squared Error, MSE)。
2. L2损失的性质
-
平方惩罚:L2损失函数通过平方差异来惩罚预测误差,这使得大的误差会产生更大的损失。这种性质对模型的优化具有重要作用,因为它会迫使模型更加重视大误差的样本。
-
平滑性:L2损失函数是连续且可导的(通常也是二阶可导的),这使得它在优化时很稳定。许多优化算法(如梯度下降)依赖于损失函数的平滑性,因此 L2 损失函数可以在许多任务中很好地工作。
-
对称性:L2损失函数的值只取决于误差的大小,不考虑方向。因此,无论预测值偏高还是偏低,损失的惩罚都是相同的。
3. L2损失函数的梯度
在机器学习模型训练过程中,我们通常使用梯度下降方法来最小化损失函数。为此,需要计算损失函数相对于模型参数的梯度。对 L2 损失函数的每一个预测值 y ^ i \hat{y}_i y^i 来说,它的梯度为:
∂ L L 2 ∂ y ^ i = 2 n ( y ^ i − y i ) \frac{\partial L_{L2}}{\partial \hat{y}_i} = \frac{2}{n} (\hat{y}_i - y_i) ∂y^i∂LL2=n2(y^i−yi)
这表明:
- 误差越大,梯度越大,因此 L2 损失会对大误差的样本进行更强的惩罚。
- 误差的符号会影响梯度的方向,若预测值高于真实值,则梯度为正,模型会朝着降低预测值的方向调整参数;若预测值低于真实值,则梯度为负,模型会朝着增大预测值的方向调整参数。
4. L2损失的应用场景
L2损失函数适用于以下场景:
-
回归任务:L2损失广泛应用于回归问题中,因为它对大的误差有更强的惩罚作用,使得模型在训练时会更加关注偏差较大的样本。例如,预测房价、股票价格等问题中,常用L2损失来最小化预测误差。
-
深度学习中的回归网络:在神经网络中,尤其是用于回归问题的神经网络,L2损失是常用的损失函数之一。
-
特征学习和表示学习:L2损失可以用于衡量特征向量或表示向量之间的相似性。在表示学习任务中,我们希望特征之间的距离能够反映数据之间的语义相似性,L2损失可以很好地完成这个任务。
5. 优势与劣势
优势
-
平滑性:L2损失函数是连续、平滑且可微的,这使得它在优化过程中非常稳定,适合使用梯度下降优化算法。
-
凸性:L2损失是一个凸函数(针对线性模型是严格凸函数),这意味着对大多数问题而言,它的最优解是唯一的,不会出现局部极小值问题。
-
对大误差的敏感性:由于平方惩罚机制,L2损失对大的误差更加敏感,有助于模型关注并减少大误差样本的影响。
劣势
-
对离群点的敏感性:由于 L2 损失会对误差进行平方,因此对离群点非常敏感。离群点会产生很大的误差,从而主导了损失的总值,导致模型被离群点牵引,可能导致欠拟合其他样本。对于受离群点影响较大的数据集,L1损失(绝对误差损失)可能是一个更好的选择。
-
可能导致欠拟合:如果数据中有较多离群点,L2损失的平方项会导致模型过度关注这些离群点,忽略其他正常样本的数据分布,最终影响模型的整体拟合效果。
Python编程基础
【一】介绍一下Python中的继承(Inheritance)思想
继承是面向对象编程(OOP)的一个核心概念,它允许一个类(称为子类或派生类)从另一个类(称为父类或基类)继承属性和方法。子类可以继承父类的特性,并且可以在此基础上添加自己的新特性,从而实现代码的重用和扩展。Python 作为一门支持面向对象编程的语言,提供了强大的继承机制。
Python中继承的优势:
- 代码重用:子类可以直接使用父类已经定义的方法和属性,避免了重复编写相同的代码片段。
- 可扩展性:子类可以在不修改父类的情况下,添加新的属性和方法,从而使得代码更具可扩展性。这样可以在不影响父类的基础上,为程序添加新的功能。
一、继承的基本概念
1. 父类(基类)
- 定义:被继承的类,提供基本的属性和方法。
- 作用:作为子类的模板,子类可以继承父类的属性和方法。
2. 子类(派生类)
- 定义:从父类继承而来的类,可以新增或重写父类的方法和属性。
- 作用:在继承父类的基础上进行扩展或修改,实现特定的功能。
3. 继承的目的
- 代码重用:避免重复编写相同的代码,提高开发效率。
- 可扩展性:通过继承,子类可以扩展父类的功能。
- 多态性:同一个方法在不同的类中可能有不同的实现,增强程序的灵活性。
二、Python 中的继承实现
1. 基本语法
在 Python 中,继承通过在类定义时指定父类来实现。
class 子类名(父类名):
# 类的定义
2. 示例
父类:
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
pass
子类:
class Dog(Animal):
def speak(self):
return f"{self.name} says Woof!"
class Cat(Animal):
def speak(self):
return f"{self.name} says Meow!"
使用子类:
dog = Dog("Buddy")
cat = Cat("Kitty")
print(dog.speak()) # 输出: Buddy says Woof!
print(cat.speak()) # 输出: Kitty says Meow!
三、继承的类型
1. 单继承
-
定义:一个子类只继承一个父类。
-
示例:
class Parent: pass class Child(Parent): pass
2. 多重继承
-
定义:一个子类继承多个父类。
-
语法:
class 子类名(父类1, 父类2, ...): pass
-
示例:
class Flyable: def fly(self): return "I can fly!" class Swimmable: def swim(self): return "I can swim!" class Duck(Flyable, Swimmable): pass duck = Duck() print(duck.fly()) # 输出: I can fly! print(duck.swim()) # 输出: I can swim!
3. 多层继承
-
定义:子类继承父类,父类再继承其父类,形成继承链。
-
示例:
class GrandParent: pass class Parent(GrandParent): pass class Child(Parent): pass
四、方法重写(Override)
- 定义:子类重新定义父类的同名方法,以实现不同的功能。
- 作用:让子类能够根据需要修改或扩展父类的方法行为。
示例:
class Vehicle:
def move(self):
print("The vehicle is moving.")
class Car(Vehicle):
def move(self):
print("The car is driving on the road.")
vehicle = Vehicle()
car = Car()
vehicle.move() # 输出: The vehicle is moving.
car.move() # 输出: The car is driving on the road.
五、调用父类的方法
-
使用
super()
函数:在子类中调用父类的方法或初始化父类。 -
语法:
class 子类名(父类名): def 方法名(self, 参数): super().方法名(参数)
示例:
class Person:
def __init__(self, name):
self.name = name
class Employee(Person):
def __init__(self, name, employee_id):
super().__init__(name) # 调用父类的构造函数
self.employee_id = employee_id
employee = Employee("Alice", "E123")
print(employee.name) # 输出: Alice
print(employee.employee_id) # 输出: E123
六、继承中的特殊方法
1. __init__
构造函数
- 继承特性:子类的
__init__
方法会覆盖父类的__init__
方法。 - 注意:如果子类定义了
__init__
方法,需要显式调用父类的__init__
方法来初始化父类的属性。
示例:
class Parent:
def __init__(self):
print("Parent init")
class Child(Parent):
def __init__(self):
super().__init__() # 调用父类的构造函数
print("Child init")
child = Child()
# 输出:
# Parent init
# Child init
2. __str__
和 __repr__
方法
- 作用:定义对象的字符串表示形式。
- 继承特性:子类可以重写这些方法,提供自定义的字符串表示。
示例:
class Animal:
def __str__(self):
return "This is an animal."
class Dog(Animal):
def __str__(self):
return "This is a dog."
dog = Dog()
print(dog) # 输出: This is a dog.
七、继承的注意事项
1. 访问权限
-
Python 中不存在像 Java 或 C++ 那样的访问修饰符(public、private、protected)。
-
以双下划线
__
开头的属性或方法被视为私有成员,不能在子类中直接访问。 -
示例:
class Parent: def __init__(self): self.__private_var = 42 class Child(Parent): def get_private_var(self): return self.__private_var # 这将引发 AttributeError child = Child() print(child.get_private_var())
2. 方法解析顺序(MRO)
- 在多重继承中,Python 使用**方法解析顺序(Method Resolution Order, MRO)**来确定属性和方法的查找顺序。
- 可以使用
类名.mro()
查看 MRO 列表。
示例:
class A:
pass
class B(A):
pass
class C(A):
pass
class D(B, C):
pass
print(D.mro())
# 输出: [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
【二】介绍一下Python中的多态(Polymorphism)思想
多态(Polymorphism) 是面向对象编程(OOP)的核心概念之一,指的是同一操作作用于不同对象时,能够产生不同的解释和行为。简单来说,多态允许我们在不考虑对象具体类型的情况下,对不同类型的对象执行相同的操作。在 Python 中,多态性通过动态类型和灵活的对象模型得以实现。
一、什么是多态?
1. 定义
- 多态性(Polymorphism):源自希腊语,意为“多种形式”。在编程中,它指的是同一操作在不同对象上具有不同的行为。
2. 多态的类型
- 编译时多态(静态多态):通过方法重载和运算符重载实现(Python 中不支持方法重载,但支持运算符重载)。
- 运行时多态(动态多态):通过继承和方法重写实现(Python 中主要通过这种方式实现多态)。
二、Python 中的多态实现
1. 动态类型和鸭子类型
- 动态类型:Python 是动态类型语言,变量的类型在运行时确定。这使得多态性更自然。
- 鸭子类型(Duck Typing):只要对象具有所需的方法或属性,就可以使用,无需关心对象的具体类型。
示例:
class Dog:
def speak(self):
return "Woof!"
class Cat:
def speak(self):
return "Meow!"
class Duck:
def speak(self):
return "Quack!"
def animal_speak(animal):
return animal.speak()
animals = [Dog(), Cat(), Duck()]
for animal in animals:
print(animal_speak(animal))
输出:
Woof!
Meow!
Quack!
- 解释:
animal_speak
函数可以接受任何具有speak
方法的对象,而不关心其具体类型。这就是鸭子类型的体现。
2. 继承和方法重写
- 继承:子类继承父类的方法和属性。
- 方法重写(Override):子类可以重写父类的方法,实现不同的行为。
示例:
class Animal:
def speak(self):
raise NotImplementedError("Subclasses must implement this method.")
class Dog(Animal):
def speak(self):
return "Woof!"
class Cat(Animal):
def speak(self):
return "Meow!"
def animal_speak(animal):
return animal.speak()
animals = [Dog(), Cat()]
for animal in animals:
print(animal_speak(animal))
输出:
Woof!
Meow!
- 解释:
Animal
类定义了一个抽象方法speak
,子类Dog
和Cat
分别实现了自己的版本。animal_speak
函数调用时,根据传入对象的类型执行对应的方法。
3. 运算符重载
- 运算符重载:在类中定义特殊方法,实现对内置运算符的重载。
示例:
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
# 重载加法运算符
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
# 重载字符串表示
def __str__(self):
return f"Vector({self.x}, {self.y})"
v1 = Vector(2, 3)
v2 = Vector(5, 7)
v3 = v1 + v2
print(v3)
输出:
Vector(7, 10)
- 解释:通过定义
__add__
方法,实现了Vector
对象的加法运算。这是 Python 的另一种多态形式。
三、鸭子类型详解
1. 概念
- 鸭子类型:如果一只鸟走起来像鸭子、游泳像鸭子、叫声像鸭子,那么这只鸟可以被称为鸭子。
- 在 Python 中:只要对象具有所需的方法或属性,就可以将其视为某种类型。
示例:
class Bird:
def fly(self):
print("Bird is flying.")
class Airplane:
def fly(self):
print("Airplane is flying.")
class Fish:
def swim(self):
print("Fish is swimming.")
def lift_off(entity):
entity.fly()
bird = Bird()
plane = Airplane()
fish = Fish()
lift_off(bird) # 输出: Bird is flying.
lift_off(plane) # 输出: Airplane is flying.
# lift_off(fish) # AttributeError: 'Fish' object has no attribute 'fly'
- 解释:
lift_off
函数可以接受任何具有fly
方法的对象。Fish
对象由于没有fly
方法,调用时会抛出AttributeError
。
四、多态性的优点
1. 提高代码的灵活性
- 可以编写与特定类型无关的代码,处理不同类型的对象。
2. 增强代码的可扩展性
- 添加新类型的对象时,无需修改现有代码,只需确保新对象实现了所需的方法。
3. 代码重用
- 通过多态,可以编写通用的函数或方法,避免重复代码。
五、抽象基类(Abstract Base Class)
- 概念:抽象基类定义了接口规范,子类必须实现特定的方法。
- 作用:确保子类实现必要的方法,提供一致的接口。
示例:
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.1416 * self.radius ** 2
shapes = [Rectangle(3, 4), Circle(5)]
for shape in shapes:
print(f"Area: {shape.area()}")
输出:
Area: 12
Area: 78.53999999999999
- 解释:
Shape
是一个抽象基类,定义了area
方法。Rectangle
和Circle
实现了该方法。通过多态,可以统一处理不同形状的面积计算。
六、Python 中不支持方法重载
- 说明:在 Python 中,方法重载(相同方法名,不同参数)并不被支持。后定义的方法会覆盖先前的方法。
- 替代方案:使用默认参数或可变参数。
示例:
class MathOperations:
def multiply(self, x, y, z=None):
if z is not None:
return x * y * z
else:
return x * y
math_ops = MathOperations()
print(math_ops.multiply(2, 3)) # 输出: 6
print(math_ops.multiply(2, 3, 4)) # 输出: 24
- 解释:通过使用默认参数,实现类似方法重载的效果。
七、方法解析顺序(MRO)在多态中的作用
- MRO(Method Resolution Order):在多重继承中,Python 按照 MRO 决定调用哪个类的方法。
- 多态与 MRO:当子类继承多个父类,且父类中有同名方法时,MRO 决定了方法的调用顺序。
示例:
class A:
def do_something(self):
print("Method from A")
class B:
def do_something(self):
print("Method from B")
class C(B, A):
pass
c = C()
c.do_something()
print(C.mro())
输出:
Method from B
[<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
- 解释:
C
继承了B
和A
,由于B
在前,调用同名方法时,B
的方法优先。
模型部署基础
【一】如何将ONNX模型从GPU切换到CPU中进行缓存?
在AI行业中,在算法服务推理运行结束后,将ONNX模型从GPU切换到CPU中进行缓存,是经典的高性能算法服务的一环。 ONNX Runtime 中,可以很方便地将模型从 GPU 切换到 CPU 上。通过 ONNX Runtime 提供的 providers
参数或 set_providers
方法,可以方便地在 GPU 和 CPU 之间切换模型的运行设备。只需指定 "CPUExecutionProvider"
就可以让模型在 CPU 上进行缓存。
方法一:在创建 InferenceSession
时指定 CPUExecutionProvider
ONNX Runtime 的 InferenceSession
支持多个计算提供者(Execution Providers),可以通过指定提供者将模型从 GPU 切换到 CPU。只需要在创建 InferenceSession
时将 providers
参数设置为 ["CPUExecutionProvider"]
,就会强制模型在 CPU 上运行。
import onnxruntime as ort
# 指定使用 CPU 运行
session = ort.InferenceSession("model.onnx", providers=["CPUExecutionProvider"])
如果之前在使用 GPU(如 "CUDAExecutionProvider"
)运行模型,将 providers
参数改为 "CPUExecutionProvider"
就可以切换到 CPU 上。
方法二:通过 set_providers
方法动态切换到 CPU
如果已经创建了使用 GPU 的 InferenceSession
,可以通过 set_providers
方法动态切换到 CPU 而不重新加载模型。
# 假设已有一个使用 GPU 的 session
session.set_providers(["CPUExecutionProvider"])
这样可以在同一个 InferenceSession
上切换到 CPU。
方法三:通过 providers
属性检查当前的计算提供者
可以使用 session.get_providers()
来查看当前可用的计算提供者,并确认是否成功切换到 CPU。
print(session.get_providers()) # 输出当前会话使用的提供者
如果输出包含 "CPUExecutionProvider"
,则表示会话已经在 CPU 上运行。
示例:完整流程
import onnxruntime as ort
# 创建一个使用 GPU 的 session
session = ort.InferenceSession("model.onnx", providers=["CUDAExecutionProvider"])
# 进行推理
outputs = session.run(None, {"input_name": input_data})
# 切换到 CPU
session.set_providers(["CPUExecutionProvider"])
# 确认提供者已切换到 CPU
print("Current providers:", session.get_providers())
【二】介绍一下Base64编码图像的原理
Base64 编码是一种用于将二进制数据转换为文本字符串的编码方法。在许多网络传输和存储应用中,Base64 编码可以使二进制数据(如图像、文件)以文本形式嵌入到 JSON、XML 或 HTML 等格式中,而不会被破坏。以下是详细介绍 Base64 编码图像的原理、工作流程、应用场景以及解码过程。
一、为什么要使用 Base64 编码图像?
- 网络传输兼容性:网络传输(尤其是 JSON、HTML 等)通常只支持文本数据,而不支持直接传输二进制数据。将图像转为 Base64 可以让它以纯文本的形式传输。
- 防止数据破坏:某些协议或格式对特殊字符敏感,如 HTTP 请求中包含非标准字符可能导致传输错误。Base64 使用 A-Z、a-z、0-9、+ 和 / 这些标准 ASCII 字符来编码数据,减少兼容性问题。
- 嵌入式图像:在 HTML 和 CSS 中,可以直接嵌入 Base64 编码的图像数据而无需外部图像文件,从而减少 HTTP 请求,优化加载速度。
二、Base64 编码的原理
Base64 的核心思想是将任意二进制数据表示为可打印的 ASCII 字符串,具体步骤如下:
- 将二进制数据分组:将二进制数据分成 3 字节(3 * 8 = 24 位)一组。
- 位分割:将每组 24 位的数据分为 4 个 6 位的块。
- Base64 字符表映射:每个 6 位的块可以表示 0 到 63 之间的一个数,用 Base64 字符表(64 个字符)将这些数映射成对应的字符。
- 填充:如果数据长度不是 3 的倍数,则会在编码结果末尾添加
=
字符,以填充 Base64 字符串长度,使其符合 4 字节的倍数。
Base64 字符表:
A-Z
表示 0-25a-z
表示 26-510-9
表示 52-61+
表示 62/
表示 63
三、图像的 Base64 编码过程
1. 获取二进制数据
图像文件本质上是一个包含像素值和元数据的二进制文件。可以通过打开图像文件并读取二进制数据的方式来获取图像的原始数据。
2. 将二进制数据编码为 Base64 字符串
将图像的二进制数据进行 Base64 编码。以 Python 为例:
import base64
# 打开图像文件并读取二进制数据
with open("example.jpg", "rb") as image_file:
binary_data = image_file.read()
# 将二进制数据编码为 Base64
base64_data = base64.b64encode(binary_data).decode('utf-8')
print(base64_data)
这里的 base64.b64encode
函数将二进制数据转换为 Base64 编码的字节对象,使用 decode('utf-8')
将其转为字符串。
3. 生成 Base64 数据 URI
为了在 HTML 中嵌入图像,可以将 Base64 编码的字符串封装为 data URI
格式:
<img src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/..." />
其中,data:image/jpeg;base64,
表示 MIME 类型为 JPEG 格式的图像,紧随其后的 Base64 字符串就是图像数据。
四、Base64 解码图像的过程
将 Base64 字符串还原成图像的步骤如下:
- 去掉 MIME 前缀:如果 Base64 字符串有 MIME 前缀(如
data:image/jpeg;base64,
),需要将其去除,只保留实际编码的数据部分。 - 解码 Base64:将 Base64 字符串解码回原始的二进制数据。
- 将二进制数据写入文件或加载为图像:可以将解码后的数据写入文件或直接加载为图像。
Python 示例
import base64
# 假设 base64_data 是从 Base64 字符串中提取的图像数据
base64_data = "..." # 这里应该是实际的 Base64 字符串
# 解码 Base64 数据
image_data = base64.b64decode(base64_data)
# 将解码后的数据保存为图像文件
with open("decoded_image.jpg", "wb") as file:
file.write(image_data)
五、优缺点
优点
- 兼容性:Base64 编码生成的字符串可以安全地嵌入文本文件,适合在 HTTP、HTML、CSS、JSON 等场景中使用。
- 减少请求:在 HTML 或 CSS 中使用 Base64 可以减少外部图像文件的 HTTP 请求,适合小图标或背景图片。
缺点
- 增加数据量:Base64 编码会使数据量增大约 33%,即原来的二进制数据需要约 4/3 的空间。
- 性能问题:Base64 图像解码需要更多的 CPU 资源和内存,尤其是较大的图像可能导致页面加载和显示变慢。
- 不适合大型文件:由于数据体积增大,Base64 编码不适合用于大型图像或视频文件。
计算机基础
Rocky从工业界、学术界、竞赛界以及应用界角度出发,总结归纳AI行业所需的计算机基础干货知识。这些干货知识不仅能在面试中帮助我们,还能让我们在AI行业中提高工作效率。
【一】介绍一下Gunicorn的原理,并举例其在AI行业的作用
Gunicorn(Green Unicorn) 是一个被广泛应用的 Python Web 服务器网关接口(WSGI)HTTP 服务器,适用于 UNIX 系统。它以简单、高效、轻量级著称,能够兼容多种 Web 框架,如 Django、Flask、Pyramid 等。
在 AI 行业,模型的部署和服务化是关键环节之一。Gunicorn 作为成熟的 Python WSGI 服务器,为 AI 模型的服务化部署提供了稳健、高性能的解决方案。通过与各种 Web 框架和异步工作类的结合,Gunicorn 能够满足 AI 应用对高并发、低延迟和稳定性的要求。在 AI 行业的实际应用中,合理地配置和使用 Gunicorn,可以有效提升模型服务的性能和可靠性。
一、Gunicorn 的原理详解
1. WSGI 概述
- WSGI(Web Server Gateway Interface) 是 Python 应用程序或框架与 Web 服务器之间的标准接口。
- 它定义了一套简单的调用约定,使得不同的 Web 服务器和应用框架可以互相兼容。
2. Gunicorn 的架构
Gunicorn 采用了 预分叉(Pre-fork) 的工作模式,其架构主要包括:
- Master(主进程):负责管理 Worker(工作进程),包括创建、监控和销毁。
- Worker(工作进程):处理实际的客户端请求,每个进程独立运行。
3. Gunicorn 的工作原理
-
启动阶段:
- Gunicorn 被启动后,创建一个 Master 进程。
- Master 根据配置,预先 Fork 出多个 Worker 进程。
-
请求处理:
- 客户端请求到达服务器后,由 Master 进程通过监听套接字(Socket)接受连接。
- Master 将连接分配给空闲的 Worker 进程。
-
响应阶段:
- Worker 进程根据 WSGI 应用程序处理请求,生成响应。
- 响应通过套接字返回给客户端。
-
进程管理:
- Master 监控 Worker 的状态,必要时重启或增减 Worker 数量。
- 支持平滑重启和代码更新,确保服务的高可用性。
4. Worker 类型
Gunicorn 支持多种 Worker 类型,以满足不同的并发需求:
- 同步(sync):默认的 Worker 类型,每个请求占用一个线程,适用于 I/O 密集型应用。
- 异步(async):基于事件循环,如 gevent、eventlet,适用于高并发场景。
- 基于线程(threads):每个 Worker 可以处理多个线程,提高并发能力。
5. 配置与定制
- 配置文件:Gunicorn 支持使用配置文件或命令行参数,灵活定制服务器行为。
- 中间件与插件:支持自定义中间件,扩展功能,如日志、监控等。
二、Gunicorn 的优势
-
高性能:采用预分叉模型,减少进程创建和销毁的开销,提供稳定的性能表现。
-
简单易用:配置简洁,易于与各种 Python Web 框架集成。
-
可扩展性:支持多种 Worker 类型,适应不同的应用场景和并发需求。
-
稳健性:具备良好的错误处理和进程管理机制,确保服务的可靠运行。
三、Gunicorn 在 AI 行业的作用
1. AI 模型的服务化部署
在 AI 应用中,将训练好的模型部署为 Web 服务,供客户端调用是常见需求。Gunicorn 在这一过程中提供了可靠的服务器环境。
- 与 Flask 或 FastAPI 集成:通过 WSGI 或 ASGI 接口,将模型封装为 API 服务。
- 处理高并发请求:Gunicorn 的多 Worker 模型能够有效处理来自不同客户端的并发请求。
- 稳定性和可靠性:确保模型服务在长时间运行下的稳定性。
2. 实例:使用 Flask 部署预测服务
# app.py
from flask import Flask, request, jsonify
import joblib
app = Flask(__name__)
model = joblib.load('model.pkl')
@app.route('/predict', methods=['POST'])
def predict():
data = request.json
prediction = model.predict([data['features']])
return jsonify({'prediction': prediction.tolist()})
if __name__ == '__main__':
app.run()
使用 Gunicorn 启动服务:
gunicorn app:app --workers 4
- 解释:
app:app
:指定模块和应用实例。--workers 4
:启动 4 个 Worker 进程,提高并发处理能力。
使用 Gunicorn 和 UvicornWorker 启动服务:
gunicorn main:app --workers 2 --worker-class uvicorn.workers.UvicornWorker
- 解释:
--worker-class uvicorn.workers.UvicornWorker
:使用异步 Worker,支持高并发和异步请求。
四、Gunicorn在AI服务部署中的优势
-
高效利用资源:通过多进程模型,充分利用多核 CPU,提高吞吐量。
-
易于扩展:可以根据需求调整 Worker 数量,灵活应对流量变化。
-
安全性:隔离不同的 Worker 进程,防止单点故障。
-
与容器化技术结合:常与 Docker 等容器技术配合,构建可移植的部署方案。
【二】介绍一下Uvicorn的原理,并举例其在AI行业的作用
Uvicorn 是一个基于 Python 的高速、轻量级 ASGI(Asynchronous Server Gateway Interface)服务器,采用了基于事件循环的异步编程模型。它以高性能和低资源消耗著称,适用于部署基于异步框架(如 FastAPI、Starlette)的 Python Web 应用。
在 AI 行业,实时性和高并发能力对于模型服务化部署尤为重要。Uvicorn 作为现代化的高性能 ASGI 服务器,在 AI 行业的应用越来越广泛。它的高并发、低延迟和对异步协议的支持,使得 AI 应用能够满足实时性和高吞吐量的要求。通过与异步框架的结合,Uvicorn 为 AI 模型的部署和服务化提供了强大的支持。
一、Uvicorn 的原理详解
1. ASGI 概述
- ASGI(Asynchronous Server Gateway Interface) 是 WSGI 的异步版本,旨在支持异步通信协议,如 WebSocket,以及高并发的异步应用。
- ASGI 定义了应用程序与服务器之间的接口标准,允许异步框架和服务器之间的互操作。
2. Uvicorn 的架构
Uvicorn 采用了现代化的异步编程技术,基于 uvloop 和 httptools,提供了高性能的网络 I/O 和 HTTP 处理。
- uvloop:一个基于 libuv 的高性能事件循环,比默认的 asyncio 事件循环快很多。
- httptools:用于解析 HTTP 协议的高效库。
3. Uvicorn 的工作原理
- 事件循环机制
- 事件循环(Event Loop):核心组件,负责调度和执行异步任务。
- Uvicorn 使用 uvloop 作为事件循环的实现,提供了高效的异步 I/O 操作。
- 异步请求处理
- 异步任务:Uvicorn 能够处理异步函数(
async def
),在请求到来时创建协程进行处理。 - 并发性:通过协程和事件循环,Uvicorn 能够在单个线程中处理大量的并发连接。
- ASGI 应用接口
- Scope:请求的上下文信息,如类型、路径、头信息等。
- Receive:异步函数,用于接收客户端发送的消息。
- Send:异步函数,用于向客户端发送消息。
Uvicorn 接受客户端请求,创建 Scope,并调用 ASGI 应用的入口点,将 scope
、receive
、send
传递给应用程序。
- 协程调度
- 协程(Coroutine):Python 中的异步函数,使用
async def
定义。 - Uvicorn 在事件循环中调度协程的执行,非阻塞地处理 I/O 操作,实现高效的资源利用。
4. Uvicorn 的特点
- 高性能:利用 uvloop 和 httptools,提供了极快的请求处理速度。
- 低资源消耗:异步编程模型减少了线程和进程的开销。
- 支持多种协议:除了 HTTP,还支持 WebSocket 等异步通信协议。
- 易于集成:与 FastAPI、Starlette 等异步框架无缝衔接。
二、Uvicorn 的优势
-
高并发处理能力
- 通过异步 I/O 和事件循环,能够在单个线程中处理大量并发请求。
-
低延迟
- 减少了上下文切换和线程调度的开销,提高了响应速度。
-
支持异步特性
- 能够处理 WebSocket 等需要保持长连接的协议,适合实时通信场景。
-
轻量级
- 安装和部署简单,启动速度快,占用资源少。
三、Uvicorn 在 AI 行业的作用
1. 实时推理服务
AI 应用常常需要提供实时的预测和推理服务,如聊天机器人、语音识别、实时推荐等。Uvicorn 的高并发和低延迟特性使其成为部署此类服务的理想选择。
示例:使用 FastAPI 部署实时预测服务
# main.py
from fastapi import FastAPI
import asyncio
import torch
app = FastAPI()
model = torch.load('model.pt')
@app.post("/predict")
async def predict(data: dict):
input_tensor = torch.tensor(data['input'])
result = await asyncio.to_thread(model_predict, input_tensor)
return {"result": result.tolist()}
def model_predict(input_tensor):
with torch.no_grad():
output = model(input_tensor)
return output
启动命令:
uvicorn main:app --host 0.0.0.0 --port 8000
- 解释:
- 使用 Uvicorn 直接运行 ASGI 应用。
--host
和--port
指定服务器监听的地址和端口。
1. 使用多进程模式
- Uvicorn 可以使用
--workers
参数启动多个进程,充分利用多核 CPU。
示例:
uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4
- 解释:
- 启动 4 个进程,提升并发处理能力。
2. 与 Gunicorn 结合
- 使用 Gunicorn 管理进程,Uvicorn 作为工作进程,提供更稳健的进程管理。
示例:
gunicorn main:app --workers 4 --worker-class uvicorn.workers.UvicornWorker
- 解释:
- Gunicorn 负责进程管理,Uvicorn 提供异步处理。
2. 异步数据处理
在一些需要处理高吞吐量数据的 AI 应用中,如日志分析、实时监控等,Uvicorn 可以与异步框架结合,实现高效的数据接收和处理。
示例:实时数据接收与处理
# main.py
from fastapi import FastAPI, WebSocket
from some_async_data_processing_library import process_data
app = FastAPI()
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
while True:
data = await websocket.receive_text()
result = await process_data(data)
await websocket.send_text(result)
启动命令:
uvicorn main:app --host 0.0.0.0 --port 8000
- 解释:
- 使用 WebSocket 接收实时数据。
- 异步处理数据,提高吞吐量。
3. 部署异步机器学习服务
一些机器学习任务,如异步训练、在线学习等,需要异步的处理方式。Uvicorn 能够支持这些异步任务的部署。
示例:异步任务队列
# main.py
from fastapi import FastAPI, BackgroundTasks
import asyncio
app = FastAPI()
@app.post("/train")
async def train_model(data: dict, background_tasks: BackgroundTasks):
background_tasks.add_task(async_train, data)
return {"message": "Training started"}
async def async_train(data):
await asyncio.sleep(10) # 模拟长时间训练任务
# 执行训练逻辑
- 解释:
- 使用
BackgroundTasks
在后台异步执行训练任务,不阻塞主线程。
- 使用
4. 与 AI 框架的集成
- FastAPI:Uvicorn 是 FastAPI 推荐的 ASGI 服务器,二者结合能够高效地部署 AI 应用。
- Starlette:作为一个轻量级的 ASGI 框架,与 Uvicorn 完美配合。
四、Uvicorn在AI部署中的优势
-
高效处理 I/O 密集型任务
- AI 应用中,经常需要处理大量的 I/O 操作,如读取数据、网络通信等。
-
简化代码
- 异步编程模型使代码更加简洁,易于维护。
-
灵活的扩展性
- 支持多种协议,方便扩展新的功能,如添加 WebSocket 支持。
-
适合微服务架构
- 轻量级的特性,使其适合在容器化环境中部署微服务。
开放性问题
Rocky从工业界、学术界、竞赛界以及应用界角度出发,思考总结AI行业的干货开放性问题,这些问题不仅能够用于面试官的提问,也可以用作面试者的提问,在面试的最后阶段让面试双方进入深度的探讨与交流。
与此同时,这些开放性问题也是贯穿我们职业生涯的本质问题,需要我们持续的思考感悟。这些问题没有标准答案,Rocky相信大家心中都有自己对于AI行业的认知与判断,欢迎大家在留言区分享与评论。
【一】AI算法工程师从纯技术到技术管理的差异是什么?
Rocky认为这是一个非常有价值的问题,不管是AIGC领域、传统深度学习领域还是自动驾驶领域,从技术到技术管理,都需要我们大刀阔斧的提升行业认知、增强思维全面性以及拔高能力基本面。
【二】AI公司的各个部门如何减小信息传递的损耗率?
在AI领域,如何有效的将价值信息传达到每个部门,并降低损耗率,是AI产品和AI算法解决方案成败的关键,也是整个业务团队效率提升的关键一招。
推荐阅读
1、加入AIGCmagic社区知识星球!
AIGCmagic社区知识星球不同于市面上其他的AI知识星球,AIGCmagic社区知识星球是国内首个以AIGC全栈技术与商业变现为主线的学习交流平台,涉及AI绘画、AI视频、大模型、AI多模态、数字人以及全行业AIGC赋能等100+应用方向。星球内部包含海量学习资源、专业问答、前沿资讯、内推招聘、AI课程、AIGC模型、AIGC数据集和源码等干货。
那该如何加入星球呢?很简单,我们只需要扫下方的二维码即可。知识星球原价:299元/年,前200名限量活动价,终身优惠只需199元/年。大家只需要扫描下面的星球优惠卷即可享受初始居民的最大优惠:
2、Sora等AI视频大模型的核心原理,核心基础知识,网络结构,经典应用场景,从0到1搭建使用AI视频大模型,从0到1训练自己的AI视频大模型,AI视频大模型性能测评,AI视频领域未来发展等全维度解析文章正式发布!
码字不易,欢迎大家多多点赞:
Sora等AI视频大模型文章地址:https://zhuanlan.zhihu.com/p/706722494
3、Stable Diffusion 3和FLUX.1核心原理,核心基础知识,网络结构,从0到1搭建使用Stable Diffusion 3和FLUX.1进行AI绘画,从0到1上手使用Stable Diffusion 3和FLUX.1训练自己的AI绘画模型,Stable Diffusion 3和FLUX.1性能优化等全维度解析文章正式发布!
码字不易,欢迎大家多多点赞:
Stable Diffusion 3和FLUX.1文章地址:https://zhuanlan.zhihu.com/p/684068402
4、Stable Diffusion XL核心基础知识,网络结构,从0到1搭建使用Stable Diffusion XL进行AI绘画,从0到1上手使用Stable Diffusion XL训练自己的AI绘画模型,AI绘画领域的未来发展等全维度解析文章正式发布!
码字不易,欢迎大家多多点赞:
Stable Diffusion XL文章地址:https://zhuanlan.zhihu.com/p/643420260
5、Stable Diffusion 1.x-2.x核心原理,核心基础知识,网络结构,经典应用场景,从0到1搭建使用Stable Diffusion进行AI绘画,从0到1上手使用Stable Diffusion训练自己的AI绘画模型,Stable Diffusion性能优化等全维度解析文章正式发布!
码字不易,欢迎大家多多点赞:
Stable Diffusion文章地址:https://zhuanlan.zhihu.com/p/632809634
6、ControlNet核心基础知识,核心网络结构,从0到1使用ControlNet进行AI绘画,从0到1训练自己的ControlNet模型,从0到1上手构建ControlNet商业变现应用等全维度解析文章正式发布!
码字不易,欢迎大家多多点赞:
ControlNet文章地址:https://zhuanlan.zhihu.com/p/660924126
7、LoRA系列模型核心原理,核心基础知识,从0到1使用LoRA模型进行AI绘画,从0到1上手训练自己的LoRA模型,LoRA变体模型介绍,优质LoRA推荐等全维度解析文章正式发布!
码字不易,欢迎大家多多点赞:
LoRA文章地址:https://zhuanlan.zhihu.com/p/639229126
8、Transformer核心基础知识,核心网络结构,AIGC时代的Transformer新内涵,各AI领域Transformer的应用落地,Transformer未来发展趋势等全维度解析文章正式发布!
码字不易,欢迎大家多多点赞:
Transformer文章地址:https://zhuanlan.zhihu.com/p/709874399
9、最全面的AIGC面经《手把手教你成为AIGC算法工程师,斩获AIGC算法offer!(2024年版)》文章正式发布!
码字不易,欢迎大家多多点赞:
AIGC面经文章地址:https://zhuanlan.zhihu.com/p/651076114
10、50万字大汇总《“三年面试五年模拟”之算法工程师的求职面试“独孤九剑”秘籍》文章正式发布!
码字不易,欢迎大家多多点赞:
算法工程师三年面试五年模拟文章地址:https://zhuanlan.zhihu.com/p/545374303
《三年面试五年模拟》github项目地址(希望大家能多多star):https://github.com/WeThinkIn/Interview-for-Algorithm-Engineer
11、Stable Diffusion WebUI、ComfyUI、Fooocus三大主流AI绘画框架核心知识,从0到1搭建AI绘画框架,从0到1使用AI绘画框架的保姆级教程,深入浅出介绍AI绘画框架的各模块功能,深入浅出介绍AI绘画框架的高阶用法等全维度解析文章正式发布!
码字不易,欢迎大家多多点赞:
AI绘画框架文章地址:https://zhuanlan.zhihu.com/p/673439761
12、GAN网络核心基础知识,网络架构,GAN经典变体模型,经典应用场景,GAN在AIGC时代的商业应用等全维度解析文章正式发布!
码字不易,欢迎大家多多点赞:
GAN网络文章地址:https://zhuanlan.zhihu.com/p/663157306
13、其他
Rocky将YOLOv1-v7全系列大解析文章也制作成相应的pdf版本,大家可以关注公众号WeThinkIn,并在后台 【精华干货】菜单或者回复关键词“YOLO” 进行取用。
Rocky一直在运营技术交流群(WeThinkIn-技术交流群),这个群的初心主要聚焦于技术话题的讨论与学习,包括但不限于算法,开发,竞赛,科研以及工作求职等。群里有很多人工智能行业的大牛,欢迎大家入群一起学习交流~(请添加小助手微信Jarvis8866,拉你进群~)
标签:__,2024.10,21,AI,self,面试,图像,--,模型 From: https://blog.csdn.net/Rocky6688/article/details/143659834