AIGC时代算法工程师的面试秘籍(第十九式2024.7.22-8.4) |【三年面试五年模拟】

文摘   科技   2024-08-04 23:04   浙江  
近期文章回顾(更多热门文章请关注公众号与知乎Rocky Ding哦)

写在前面

【三年面试五年模拟】旨在整理&挖掘AI算法工程师在实习/校招/社招时所需的干货知识点与面试方法,力求让读者在获得心仪offer的同时,增强技术基本面。也欢迎大家提出宝贵的优化建议,一起交流学习💪

WeThinkIn最新福利放送:大家只需关注WeThinkIn公众号,后台回复“简历资源”,即可获取包含Rocky独家简历模版在内的60套精选的简历模板资源,希望能给大家在AIGC时代带来帮助。

大家好,我是Rocky。

又到了定期阅读《三年面试五年模拟》文章的时候了!本周期共更新了60多个AIGC面试高频问答,依旧干货满满!诚意满满!

《三年面试五年模拟》系列文章帮助很多读者获得了心仪的算法岗offer,收到了大家的很多好评,Rocky觉得很开心也很有意义。

在AIGC时代到来后,Rocky对《三年面试五年模拟》整体战略方向进行了重大的优化重构,在秉持着Rocky创办《三年面试五年模拟》项目初心的同时,增加了AIGC时代核心的版块栏目,详细的版本更新内容如下所示:

  1. 整体架构:分为AIGC知识板块和AI通用知识板块。
  2. AIGC知识板块:分为AI绘画、AI视频、大模型、AI多模态、数字人这五大AIGC核心方向。
  3. AI通用知识板块:包含AIGC、传统深度学习、自动驾驶等所有AI核心方向共通的知识点。

Rocky已经将《三年面试五年模拟》项目的完整版构建在Github上:https://github.com/WeThinkIn/Interview-for-Algorithm-Engineer/tree/main,本周期更新的60+AIGC面试高频问答已经全部同步到项目中了,欢迎大家star!

本文是《三年面试五年模拟》项目的第十九式,考虑到易读性与文章篇幅,Rocky本次只从Github完整版项目中摘选了2024年7月22号-2024年8月4号更新的部分经典&干货面试知识点和面试问题,并配以相应的参考答案(精简版),供大家学习探讨。

《三年面试五年模拟》版本更新白皮书,迎接AIGC时代中我们阐述了《三年面试五年模拟》项目在AIGC时代的愿景与规划,也包含了项目共建计划,感兴趣的朋友可以一起参与本项目的共建!

当然的,本项目中的内容难免有疏漏与错误之处,欢迎大家在文末评论进行补充优化,Rocky将及时更新完善到Github上!

希望《三年面试五年模拟》能陪伴大家度过整个AI行业的职业生涯,并且让大家能够持续获益。

So,enjoy(与本文的BGM一起食用更佳哦):

正文开始

目录先行

AI绘画基础:

  1. 长宽比分桶训练策略(AspectRatioBucketing)有什么作用?

  2. 介绍一下长宽比分桶训练策略(AspectRatioBucketing)的具体流程

AI视频基础:

  1. Sora在训练时是如何处理输入数据的?

  2. Sora是如何对视频数据进行标注的?

深度学习基础:

  1. 介绍一下MLP网络

  2. 人工智能、机器学习以及深度学习这三者是什么样的关系?

机器学习基础:

  1. 介绍一下机器学习中的特征编码与特征解码的概念

  2. 什么是机器学习中的鞍点?

Python编程基础:

  1. python如何清理AI模型的显存占用?

  2. 介绍一下Python中的引用计数原理,如何消除一个变量上的所有引用计数?

模型部署基础:

  1. 英伟达的GPU架构是什么样的?

  2. 轻量级模型部署经验分享

计算机基础:

  1. 如何使用Git将AI项目切换到特定分支?

  2. 在AI项目中,从云端读取传输信息失败,显示网络不可用,一般可以怎么解决呢?

开放性问题:

  1. AI算法工程师如何跨过时代周期?

  2. AIGC、传统深度学习、自动驾驶这三个方向在落地模式上有哪些异同?

AI绘画基础

【一】长宽比分桶训练策略(AspectRatioBucketing)有什么作用?

目前AI绘画开源社区中很多的LoRA模型和Stable Diffusion模型都是基于单一图像分辨率(比如1:1)进行训练的,这就导致当我们想要生成不同尺寸分辨率的图像(比如1:2、3:4、4:3、9:16、16:9等)时,非常容易生成结构崩坏的图像内容。 如下图所示,为了让所有的数据满足特定的训练分辨率,会进行中心裁剪和随机裁剪等操作,这就导致图像中人物的重要特征缺失

这上面这种情况下,我们训练的LoRA模型和Stable Diffusion模型在生成骑士图像的时候,就会出现缺失的骑士特征。

与此同时,裁剪后的图像还会导致图像内容与标签内容的不匹配,比如原本描述图像的标签中含有“皇冠”,但是显然裁剪后的图像中已经不包含皇冠的内容了。

长宽比分桶训练策略(Aspect Ratio Bucketing)就是为了解决上面的问题孕育而生。长宽比分桶训练策略的本质是多分辨率训练,就是在LoRA模型的训练过程中采用多分辨率而不是单一分辨率,多分辨率训练技术在传统深度学习时代的目标检测、图像分割、图像分类等领域非常有效,在AIGC时代终于有了新的内涵,在AI绘画领域重新繁荣。

【二】介绍一下长宽比分桶训练策略(AspectRatioBucketing)的具体流程

AI绘画领域中的长宽比分桶训练策略主要通过数据分桶+多分辨率训练两者结合来实现。我们设计多个存储桶(Bucket),每个存储桶代表不同的分辨率(比如512x512、768x768、1024x1024等),并将数据存入对应的桶中。在Stable Diffusion模型和LoRA模型训练时,随机选择一个桶,从中采样Batch大小的数据用于多分辨率训练。下面Rocky详细介绍一下完整的流程。

我们先介绍如何对训练数据进行分桶,这里包含存储桶设计数据存储两个部分。

首先我们需要设置存储桶(Bucket)的数量和每个存储桶代表的分辨率。我们定义最大的整体图像像素为1024x1024,最大的单边分辨率为1024。

这时我们以64像素为标准,设置长度为1024不变,宽度以1024为起点,根据数据集中的最小宽度设计存储桶(假设为512),具体流程如下所示:

设置长度为 1024,设置宽度为 1024
设置桶数量为 0
当宽度大于数据集最小宽度 512 时:
    宽度 = 宽度 - 64 ( 960 )
    那么 ( 960 , 1024 )作为一个存储桶的分辨率
    以此类推设计出长度不变,宽度持续自适应的存储桶

按照上面的流程,我们可以获得如下的存储桶:

bucket 0 (512, 1024)
bucket 1 (576, 1024)
bucket 2 (640, 1024)
bucket 3 (704, 1024)
bucket 4 (768, 1024)
bucket 5 (832, 1024)
bucket 6 (896, 1024)
bucket 7 (960, 1024)

接着我们再以64像素为标准,设置宽度为1024不变,长度以1024为起点,根据数据集中的最小长度设计存储桶(假设为512),按照上面相同的规则,设计对应的存储桶:

bucket 8 (1024, 512)
bucket 9 (1024, 576)
bucket 10 (1024, 640)
bucket 11 (1024, 704)
bucket 12 (1024, 768)
bucket 13 (1024, 832)
bucket 14 (1024, 896)
bucket 15 (1024, 960)

最后我们再将1024x1024分辨率作为一个存储桶添加到分桶列表中,从而获得完整的分桶列表:

bucket 0 (512, 1024)
bucket 1 (576, 1024)
bucket 2 (640, 1024)
bucket 3 (704, 1024)
bucket 4 (768, 1024)
bucket 5 (832, 1024)
bucket 6 (896, 1024)
bucket 7 (960, 1024)
bucket 8 (1024, 512)
bucket 9 (1024, 576)
bucket 10 (1024, 640)
bucket 11 (1024, 704)
bucket 12 (1024, 768)
bucket 13 (1024, 832)
bucket 14 (1024, 896)
bucket 15 (1024, 960)
bucket 16 (1024, 1024)

完成了分桶的数量与分辨率设计,我们接下来要做的是将数据集中的图片存储到对应的存储桶中

那么,具体是如何将不同分辨率的图片放入对应的桶中呢?

我们首先计算存储桶分辨率的长宽比,对于数据集中的每个图像,我们也计算其长宽比。这时我们将长宽比最接近的数据与存储桶进行匹配,并将图像存入对应的存储桶中,下面的计算过程代表寻找与数据长宽比最接近的存储桶:

如果图像的长宽比与最匹配的存储桶的长宽比差异依然非常大,则从数据集中删除该图像。所以我们最好在数据分桶前将数据进行精细化筛选,增加数据的利用率。

当image_aspect与bucket_aspects完全一致时,可以直接将图片放入对应的存储桶中;当image_aspect与bucket_aspects不一致时,需要对图片进行中心裁剪,获得与存储桶一致的长宽比,再放入存储桶中。中心裁剪的过程如下图所示:

由于我们以经做了精细化的存储桶设计,所以出现长宽比不匹配时的图像裁剪比例一般小于0.033,只去除了小于32像素的实际图像内容,所以对训练影响不大

在完成数据的分桶存储后,接下来Rocky再讲解一下在训练过程中如何基于存储桶实现多分辨率训练过程

在Stable Diffusion模型和LoRA模型的训练过程中,我们需要从刚才设计的16个存储桶中随机采样一个存储桶,并且确保每次能够提供一个完整的Batch数据。当遇到选择的存储桶中数据数量不够Batch大小的情况,需要进行特定的数据补充策略

为了解决上述的问题,我们需要维护一个公共桶(remaining bucket),其他存储桶中的数据量不足Batch大小时,将剩余的数据全部放到这个公共桶中。在每次迭代的时候,如果是从常规存储桶中取出数据,则训练分辨率调整成存储桶对应的分辨率。如果是从公共桶中取出,则训练分辨率调整成设计分桶时的基础分辨率,也就是1024x1024。

同时我们将所有的存储桶根据桶中数据量进行权重设置,具体的权重计算方式为这个存储桶的数据量除以所有剩余存储桶的数据量总和。如果不通过权重来选择存储存储桶,数据量小的存储桶会在训练过程的早期就被用完,而数据量最大的存储桶会在训练结束时仍然存在,这就会导致存储桶在整个训练周期中采样不均衡问题。通过按数据量加权选择桶可以避免这种情况。

AI视频基础

【一】Sora在训练时是如何处理输入数据的?

Sora在处理输入数据的过程中引入了大语言模型标配的Tokenizer思想。

在文本对话领域,Tokenizer可使任何长度和内容的文本编码成大语言模型可以直接处理(输入/输出)的Text Embeddings特征。在AI视频领域则是将视频数据进行编码获得visual patches,下图展示了Sore将输入视频转换成visual patches的过程:

其中先使用一个Visual Encoder模型将视频数据(空间和时间维度)压缩编码到Latent特征空间,获得一个3D visual patch array,接着将整个Latent特征分解成spacetime patches,最后再排列组合成为一个visual patches向量。

为了能有一个高质量的视频数据压缩编码效果,OpenAI针对性训练了一个Video compression network作为Visual Encoder模型。同时也训练了一个Visual Decoder模型用于Sora的解码来获取生成的视频结果。

Sora通过对输入数据的压缩编码,为AI视频的生成带来了很多帮助:

  1. 训练的数据分辨率获得了解放: Sora能够训练任意的分辨率、时间长度和长宽比的视频/图像数据。
  2. 灵活的生成分辨率: Sora可以生成1920x1080像素(横屏)到1080x1920像素(竖屏)之间任意分辨率的视频。
  3. 生成视频的边缘更加符合真实常理: Sora尝试过固定分辨率进行训练,这种情况下就需要裁剪视频数据。这样数据的裁剪bias会被带入到模型中,导致Sora模型生成很多主要内容缺失的视频。

【二】Sora是如何对视频数据进行标注的?

和AI绘画领域的DALL-E 3一样,OpenAI使用内部的数据标注模型(大概率是GPT4-v等)给视频数据进行了详细的描述(Caption),从而提升了Sora模型生成的视频与输入Prompt的一致性,同时生成视频的质量与视频中文本的渲染能力也有一定的提升。

Rocky认为视频数据的精细化标注非常关键,是AI视频模型生成效果的关键一招,因为视频生成的动态性非常高,是一个非常大的数据分布

与此同时,虽然在训练中使用详细的描述(Caption)作为数据标签能够增强AI视频模型的文本一致性能力,但是在用户使用时可能会出现bias,因为用户输入的描述一般都相对较短。这里Sora和DALL-E 3一样,也使用GPT-4来对用户的输入描述进行扩充完善来解决这个问题,同时提高了用户的使用体验和视频生成的多样性

深度学习基础

【一】介绍一下MLP网络

多层感知器(Multilayer Perceptron, MLP)可以说是最基本的神经网络,由Frank Rosenblatt于1957提出,一直广泛应用于各种机器学习和深度学习任务中。

在传统深度学习时代,由于卷积神经网络的出现,一定程度上MLP网络的使用频率有所减少,但是在Transformer发布后,MLP结构重新站上AI行业的舞台。在现在的AIGC时代中,MLP结构已经成为AIGC模型的重要组成部分

下面是Rocky对MLP网络的详细讲解:

1. MLP的基本结构

MLP由一个输入层、一个或多个隐藏层和一个输出层组成。每一层都包含多个神经元(或节点),这些神经元之间是全连接的,即每个神经元的输出连接到下一层的每个神经元。

1.1 输入层

输入层的神经元数等于输入数据的特征数。如果输入是一个包含28x28像素的图像,则输入层的神经元数为784。

1.2 隐藏层

隐藏层由一个或多个层组成,每层包含若干个神经元。隐藏层的数量和每层的神经元数是超参数,我们可以根据不同的场景进行对应的设置。隐藏层通过激活函数(如ReLU、Sigmoid、Tanh等)引入非线性,使得MLP能够学习复杂的分布和特征。

1.3 输出层

输出层的神经元数取决于具体的AI任务。例如,对于二分类任务,输出层通常包含一个神经元;对于多分类任务,输出层的神经元数等于类别数。

2. MLP的工作原理

MLP的工作原理基于前向传播和反向传播两个过程。

2.1 前向传播

在前向传播过程中,输入数据通过网络层层传递,经过每一层的加权和激活函数计算,最终得到输出结果。具体步骤如下:

  1. 加权求和:每个神经元接收前一层的输出,通过权重进行加权求和,并加上一个偏置项。

其中, 是权重, 是输入, 是偏置项。

  1. 激活函数:对加权求和的结果应用激活函数,引入非线性。

其中, 是激活函数,常见的激活函数包括ReLU、Sigmoid和Tanh等。

  1. 输出:将激活函数的输出传递给下一层,直到最后一层得到最终输出。

2.2 反向传播

在反向传播过程中,网络通过计算损失函数的梯度来更新权重和偏置项,以最小化预测误差。具体步骤如下:

  1. 损失函数:计算网络的输出与实际标签之间的损失。常见的损失函数包括均方误差(MSE)和交叉熵损失。

其中, 是实际标签, 是网络输出。

  1. 梯度计算:通过链式法则计算每个权重和偏置项的梯度。
  1. 权重更新:使用梯度下降算法更新权重和偏置项。

其中, 是学习率。

【二】人工智能、机器学习以及深度学习这三者是什么样的关系?

深度学习(Deep Learning)、机器学习(Machine Learning)和人工智能(Artificial Intelligence, AI)是三个相关但不同的概念。它们之间的关系可以理解为递进关系,人工智能是一个广义的概念,机器学习是实现人工智能的一种方法,而深度学习是机器学习的一个子集

1. 人工智能(Artificial Intelligence)

定义:人工智能是指通过计算机模拟和实现人类智能的技术和方法。它涉及使计算机系统能够执行需要人类智能的任务,如感知、推理、学习、规划和决策等。

目标:开发能够自动执行复杂任务的系统,从而在不需要人类干预的情况下完成这些任务。

2. 机器学习(Machine Learning)

定义:机器学习是人工智能的一个分支,涉及机器学习算法和模型,使计算机能够通过经验(数据)进行学习和预测,而无需明确编程。

核心概念:通过数据驱动的方法,机器学习算法能够自动调整和优化模型,以提高在特定机器学习任务上的性能。

3. 深度学习(Deep Learning)

定义:深度学习是机器学习的一个子集,使用多层神经网络模型来模拟人脑的结构和功能,从数据中自动学习和提取特征。

特点

  • 深层结构:使用多个隐藏层的神经网络来捕捉数据的复杂模式和特征。
  • 自动特征提取:能够从原始数据中自动提取特征,而无需手工特征工程。
  • 大规模数据和计算:需要大量数据和计算资源进行训练,通常依赖于GPU加速。

主要架构

  • 卷积神经网络(CNN)
  • 循环神经网络(RNN)
  • 生成对抗网络(GAN)
  • 自编码器(Autoencoder)
  • Transformers

4. 人工智能(Artificial Intelligence)、机器学习(Machine Learning)、深度学习(Deep Learning)三者之间的关系

层级关系

  • 人工智能(AI):是一个广义的领域,涵盖了所有使机器表现出智能行为的技术。
    • 深度学习(DL):是机器学习的一个子集,通过使用多层神经网络来自动学习和提取数据特征。
    • 机器学习(ML):是实现人工智能的一种方法,通过数据驱动的方式让机器学习和预测。

图示关系

人工智能(AI)
├── 机器学习(ML)
│   ├── 监督学习
│   ├── 无监督学习
│   ├── 强化学习
│   └── 深度学习(DL)
│       ├── 卷积神经网络(CNN)
│       ├── 循环神经网络(RNN)
│       ├── 生成对抗网络(GAN)
        ├── Transformers
│       └── 自编码器(Autoencoder)

机器学习基础

【一】介绍一下机器学习中的特征编码与特征解码的概念

在机器学习中,特征编码和特征解码是数据预处理的重要环节。特征编码(Feature Encoding)是将原始数据转换为AI模型能够处理的数值形式,而特征解码(Feature Decoding)是将AI模型输出的数值形式转换回原始数据形式或可解释的形式。

特征编码(Feature Encoding)

特征编码是指将原始数据转换成数值形式,以便模型可以处理。不同类型的数据需要不同的编码方法。

1. 数值特征编码

数值特征通常可以直接使用,但为了提高模型性能,常进行标准化或归一化。

  • 标准化(Standardization):将数据转换为均值为0,标准差为1的分布。

    其中, 是原始数据, 是均值, 是标准差。

  • 归一化(Normalization):将数据缩放到特定范围(通常是0到1)。

2. 类别特征编码

类别特征需要转换为数值形式,常用的方法有:

  • 独热编码(One-Hot Encoding):将每个类别转换为一个独热向量。例如,类别ABC可以转换为 [1,0,0][0,1,0][0,0,1]

  • 标签编码(Label Encoding):将每个类别转换为一个唯一的整数。例如,类别ABC可以转换为 012

  • 目标编码(Target Encoding):将类别特征编码为目标变量的均值。常用于目标是数值回归问题时。

3. 序列特征编码

对于时间序列或文本数据,通常使用嵌入或特定的预处理方法,如:

  • 词嵌入(Word Embedding):如Word2Vec、GloVe、BERT等,将单词映射到一个高维向量空间中。

  • 序列填充(Padding):将不同长度的序列填充到相同长度,以便批处理。常用于RNN、LSTM等模型。

特征解码(Feature Decoding)

特征解码是将模型的输出转换回原始数据形式或可解释的形式。

1. 数值特征解码

如果在编码过程中对数据进行了标准化或归一化,需要在解码时反向转换:

  • 反标准化

  • 反归一化

2. 类别特征解码

对于类别特征的解码,常用的方法有:

  • 独热编码解码:将独热向量转换回原始类别。例如, [1,0,0] 解码为类别 A

  • 标签编码解码:将整数标签转换回原始类别。例如, 0 解码为类别 A

3. 序列特征解码

对于序列数据,解码过程可能涉及将嵌入向量转换回单词或其他序列元素:

  • 嵌入解码:将嵌入向量转换回对应的单词或类别。

  • 序列生成:在自然语言处理任务中,生成文本序列(如机器翻译、文本生成等)。

实际应用示例

数值特征编码和解码

import numpy as np

# 数据标准化
def standardize(data):
    mean = np.mean(data)
    std = np.std(data)
    standardized_data = (data - mean) / std
    return standardized_data, mean, std

# 数据反标准化
def destandardize(standardized_data, mean, std):
    original_data = standardized_data * std + mean
    return original_data

data = np.array([12345])
standardized_data, mean, std = standardize(data)
print("Standardized Data:", standardized_data)
original_data = destandardize(standardized_data, mean, std)
print("Original Data:", original_data)

类别特征编码和解码

from sklearn.preprocessing import LabelEncoder, OneHotEncoder

# 标签编码
label_encoder = LabelEncoder()
labels = ['cat''dog''fish']
encoded_labels = label_encoder.fit_transform(labels)
print("Label Encoded:", encoded_labels)

# 标签解码
decoded_labels = label_encoder.inverse_transform(encoded_labels)
print("Label Decoded:", decoded_labels)

# 独热编码
onehot_encoder = OneHotEncoder(sparse=False)
encoded_labels = encoded_labels.reshape(len(encoded_labels), 1)
onehot_labels = onehot_encoder.fit_transform(encoded_labels)
print("One-Hot Encoded:", onehot_labels)

【二】什么是机器学习中的鞍点?

在机器学习的算法优化中,鞍点(saddle point)是一个非常重要的概念,特别是在训练深度学习模型时。理解鞍点对于理解优化过程的困难性和改进优化算法至关重要。

鞍点的定义

在数学和优化领域,鞍点是一个既不是局部最小值也不是局部最大值的点,但在某些方向上表现为最小值,而在其他方向上表现为最大值

鞍点正式的定义如下:

  • 对于函数 ,点 是鞍点,如果在 点,函数在某些方向上的二次导数为正(表示该方向上的局部最小值),而在其他方向上的二次导数为负(表示该方向上的局部最大值)。

鞍点的几何解释

从几何角度上看,鞍点类似于马鞍的形状。在二维空间中,可以将鞍点想象为一个山谷和山脊交汇的地方。沿着一个方向,它是山谷(局部最小值),沿着另一个方向,它是山脊(局部最大值)。

鞍点对机器学习模型的影响

在训练机器学习模型时,损失函数往往是高维非凸函数,具有许多局部最小值、鞍点和平坦区域。鞍点对优化算法(如梯度下降)造成了以下几个影响:

  1. 训练困难

  • 梯度下降法在鞍点附近的收敛速度会变得非常慢,因为梯度在鞍点处为零,导致更新步长变小,优化过程停滞。
  • 鞍点数量众多

    • 高维空间中的鞍点数量比局部最小值多得多,因此优化算法更容易遇到鞍点而非局部最小值。
  • 逃离鞍点

    • 使用优化算法如随机梯度下降(SGD),通过引入随机噪声,能够帮助算法逃离鞍点。这是因为噪声可以帮助突破平坦区域,继续朝着下降方向前进。

    鞍点与局部最小值的区别

    局部最小值是指在一个小范围内,函数值小于或等于该范围内其他点的值。鞍点虽然在某些方向上是最小值,但在其他方向上是最大值,因此既不是纯粹的最小值也不是最大值。

    实例与图示

    简单的二维示例

    以函数 为例。

    • 函数在原点 处有一个鞍点。
    • 沿 方向,函数表现为 ,在 时是最小值。
    • 沿 方向,函数表现为 ,在 时是最大值。

    三维图示

    在三维空间中,函数 的图形如下所示:

    import numpy as np
    import matplotlib.pyplot as plt
    from mpl_toolkits.mplot3d import Axes3D

    x = np.linspace(-22400)
    y = np.linspace(-22400)
    X, Y = np.meshgrid(x, y)
    Z = X**2 - Y**2

    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')
    ax.plot_surface(X, Y, Z, cmap='viridis')

    ax.set_xlabel('X axis')
    ax.set_ylabel('Y axis')
    ax.set_zlabel('Z axis')
    ax.set_title('Saddle Point Example')

    plt.show()
    机器学习中的鞍点示意图

    在这个图中, 处的点即为鞍点。我们可以看到,在 方向是一个上凸的曲线(局部最小值),在 方向是一个下凸的曲线(局部最大值)。

    Python编程基础

    【一】python如何清理AI模型的显存占用?

    在AIGC、传统深度学习、自动驾驶领域,在AI项目服务的运行过程中,当我们不再需要使用AI模型时,可以通过以下两个方式来释放该模型占用的显存:

    1. 删除AI模型对象、清除缓存,以及调用垃圾回收(Garbage Collection)来确保显存被释放。
    2. 将AI模型对象从GPU迁移到CPU中进行缓存。

    1. 第一种方式(删除清理)

    import torch
    import gc

    # 定义一个简单的模型
    class SimpleModel(torch.nn.Module):
        def __init__(self):
            super(SimpleModel, self).__init__()
            self.fc1 = torch.nn.Linear(1010)
            self.fc2 = torch.nn.Linear(101)
        
        def forward(self, x):
            x = torch.relu(self.fc1(x))
            x = self.fc2(x)
            return x

    # 创建模型并将其移动到 GPU
    model = SimpleModel().cuda()

    # 模拟训练或推理
    dummy_input = torch.randn(110).cuda()
    output = model(dummy_input)

    # 删除模型
    del model

    # 清除缓存
    # 使用 `torch.cuda.empty_cache()` 来清除未使用的显存缓存。这不会释放显存,但会将未使用的缓存显存返回给 GPU,以便其他 CUDA 应用程序可以使用。
    torch.cuda.empty_cache()

    # 调用垃圾回收
    # 使用 Python 的 `gc` 模块显式调用垃圾回收器,以确保删除模型对象后未引用的显存能够被释放:
    gc.collect()

    # 额外说明
    # `torch.cuda.empty_cache()`: 这个函数会释放 GPU 中缓存的内存,但不会影响已经分配的内存。它将缓存的内存返回给 GPU 以供其他 CUDA 应用程序使用。
    # `gc.collect()`: Python 的垃圾回收器会释放所有未引用的对象,包括 GPU 内存。如果删除对象后显存没有立即被释放,调用 `gc.collect()` 可以帮助确保显存被释放。

    # 检查显存使用情况
    print(torch.cuda.memory_allocated())
    print(torch.cuda.memory_reserved())

    1. 第二种方式(迁移清理)

    import torch
    import gc

    # 定义一个简单的模型
    class SimpleModel(torch.nn.Module):
        def __init__(self):
            super(SimpleModel, self).__init__()
            self.fc1 = torch.nn.Linear(1010)
            self.fc2 = torch.nn.Linear(101)
        
        def forward(self, x):
            x = torch.relu(self.fc1(x))
            x = self.fc2(x)
            return x

    # 创建模型并将其移动到 GPU
    model = SimpleModel().cuda()

    # 模拟训练或推理
    dummy_input = torch.randn(110).cuda()
    output = model(dummy_input)

    # 迁移模型
    model.cpu()

    # 清除缓存
    # 使用 `torch.cuda.empty_cache()` 来清除未使用的显存缓存。这不会释放显存,但会将未使用的缓存显存返回给 GPU,以便其他 CUDA 应用程序可以使用。
    torch.cuda.empty_cache()

    # 调用垃圾回收
    # 使用 Python 的 `gc` 模块显式调用垃圾回收器,以确保删除模型对象后未引用的显存能够被释放:
    gc.collect()

    # 额外说明
    # `torch.cuda.empty_cache()`: 这个函数会释放 GPU 中缓存的内存,但不会影响已经分配的内存。它将缓存的内存返回给 GPU 以供其他 CUDA 应用程序使用。
    # `gc.collect()`: Python 的垃圾回收器会释放所有未引用的对象,包括 GPU 内存。如果删除对象后显存没有立即被释放,调用 `gc.collect()` 可以帮助确保显存被释放。

    # 检查显存使用情况
    print(torch.cuda.memory_allocated())
    print(torch.cuda.memory_reserved())

    【二】介绍一下Python中的引用计数原理,如何消除一个变量上的所有引用计数?

    Python中的引用计数是垃圾回收机制的一部分,用来跟踪对象的引用数量。

    引用计数原理

    1. 创建对象:当创建一个对象时,其引用计数初始化为1。
    2. 增加引用:每当有一个新的引用指向该对象时(例如,将对象赋值给一个变量或将其添加到一个数据结构中),对象的引用计数增加。
    3. 减少引用:每当一个引用不再指向该对象时(例如,变量被重新赋值或被删除),对象的引用计数减少。
    4. 删除对象:当对象的引用计数降到0时,表示没有任何引用指向该对象,Python的垃圾回收器就会销毁该对象并释放其占用的内存。

    实现引用计数的例子

    # 创建对象
    a = [123]  # 引用计数为1

    # 增加引用
    b = a          # 引用计数为2
    c = a          # 引用计数为3

    # 减少引用
    del b          # 引用计数为2
    c = None       # 引用计数为1
    del a          # 引用计数为0,对象被销毁

    获取对象的引用计数

    可以使用sys模块中的getrefcount函数来获取对象的引用计数:

    import sys

    a = [123]
    print(sys.getrefcount(a))  # 通常会比实际引用多1,因为getrefcount本身也会创建一个临时引用

    如何消除一个变量上的所有引用计数

    为了确保一个对象上的所有引用都被清除,可以执行以下步骤:

    1. 删除所有变量引用:使用del语句删除所有引用该对象的变量。
    2. 清除容器引用:如果对象存在于容器(如列表、字典、集合)中,则需要从这些容器中移除对象。
    3. 关闭循环引用:如果对象存在循环引用(即对象相互引用),需要手动断开这些引用,或使用Python的垃圾回收器来处理。
    import gc

    # 创建对象并引用
    a = [123]
    b = a
    c = {'key': a}

    # 删除变量引用
    del a
    del b

    # 移除容器引用
    del c['key']

    # 强制垃圾回收以清除循环引用
    gc.collect()

    使用上述方法,可以确保对象的引用计数降为0,并且对象被销毁和内存被释放。

    循环引用问题

    循环引用会导致引用计数无法正常工作,这时需要依靠Python的垃圾回收器来检测和处理循环引用。

    import gc

    class Node:
        def __init__(self, value):
            self.value = value
            self.next = None

    # 创建循环引用
    node1 = Node(1)
    node2 = Node(2)
    node1.next = node2
    node2.next = node1

    # 删除变量
    del node1
    del node2

    # 强制垃圾回收以处理循环引用
    gc.collect()

    在上述代码中,node1node2相互引用,形成了一个循环引用。即使删除了node1node2,它们也不会被立即销毁,因为引用计数不为0。这时,需要调用gc.collect()来强制垃圾回收器处理这些循环引用。

    模型部署基础

    【一】英伟达的GPU架构是什么样的?

    英伟达(NVIDIA)的GPU架构在计算机图形学、AI模型训练与推理、高性能计算等领域有广泛的应用。NVIDIA的GPU架构在计算性能、能效和功能性方面不断进化,每一代架构都带来了显著的改进和新特性。从最早的Tesla到最新的Ampere,并通过优化CUDA核心、引入Tensor核心和RT核心,同时采用先进的内存和互连技术,NVIDIA GPU提供了强大的计算能力和灵活的应用支持。

    1. GPU架构的基本组成

    一个典型的NVIDIA GPU由多个基本组件组成,这些组件协同工作来执行高效的并行计算任务。主要组件包括:

    1.1 流处理器(Streaming Multiprocessors, SMs)

    SM是GPU的核心计算单元。每个SM包含多个CUDA核心(CUDA Cores),它们负责执行大多数计算任务。一个SM通常还包括以下单元:

    • CUDA核心:用于执行整数和浮点数运算。
    • 特殊功能单元(SFU):用于执行特殊函数,如三角函数和平方根。
    • 纹理单元(Texture Units):用于纹理采样和过滤。
    • 共享内存和寄存器文件:用于快速存储和访问中间计算结果。

    1.2 全局内存(Global Memory)

    全局内存是GPU上所有线程都可以访问的内存空间。它的访问速度相对较慢,但容量较大,通常用于存储输入数据和计算结果。

    1.3 统一内存(Unified Memory)

    统一内存允许CPU和GPU共享相同的内存空间,简化了数据管理和传输。

    1.4 L2 缓存(L2 Cache)

    L2缓存是一个共享缓存,位于全局内存和SM之间,用于加速数据访问,减少全局内存访问的延迟。

    1.5 其他单元

    • 指令调度单元(Instruction Scheduler):负责将指令分发给CUDA核心和其他单元。
    • 数据路径(Data Path):用于在不同的内存层次和计算单元之间传输数据。

    下面是GPU和CPU架构的直观对比:

    2. GPU主要架构演进

    2.1 Tesla 架构

    Tesla 架构是NVIDIA推出的首个大规模并行计算架构,标志着GPU从固定功能图形处理器向通用并行计算设备的转变。Tesla架构引入了CUDA核心,使得编程模型更加灵活。

    2.2 Fermi 架构

    Fermi 架构引入了许多关键改进,包括支持ECC内存、更大的寄存器文件、L1和L2 缓存、以及双精度浮点运算的增强。它还改进了并行计算能力,使得GPU更适合科学计算和工程应用。

    2.3 Kepler 架构

    Kepler 架构进一步提升了性能和能效,引入了动态并行性和 Hyper-Q 技术。动态并行性允许 GPU 在运行时生成新任务,Hyper-Q 则通过增加硬件工作队列的数量,显著提高了并行计算效率。

    2.4 Maxwell 架构

    Maxwell 架构专注于能效的提升和性能优化,引入了统一内存和 NVLink 技术。统一内存简化了内存管理,NVLink 提供了高带宽的 CPU-GPU 以及 GPU-GPU 连接。

    2.5 Pascal 架构

    Pascal 架构是 Maxwell 架构的改进版,增加了对混合精度计算的支持,并引入了基于 HBM2 高带宽内存的新型 GPU。Pascal 架构显著提高了深度学习的计算效率。

    2.6 Volta 架构

    Volta 架构引入了 Tensor 核心,这是一种专为深度学习设计的硬件单元,显著提升了训练和推理的性能。Volta 还改进了 CUDA 核心和 NVLink 技术,进一步增强了 GPU 的计算能力和数据传输性能。

    2.7 Turing 架构

    Turing 架构引入了实时光线追踪(Ray Tracing)技术和专用的 RT 核心,以及用于 AI 加速的 Tensor 核心。这使得 Turing 架构在图形处理和深度学习应用中表现出色。

    2.8 Ampere 架构

    Ampere 架构是 NVIDIA 最新一代 GPU 架构,进一步提升了 Tensor 核心和 RT 核心的性能,并引入了第三代 NVLink 和 PCIe 4.0 支持。Ampere 架构在图形处理、深度学习和高性能计算方面均有显著提升。

    3. GPU架构的细节

    以 Ampere 架构为例,详细介绍其关键特性和改进。

    3.1 CUDA 核心

    Ampere 架构中的 CUDA 核心经过优化,支持更高效的整数和浮点数运算。每个 SM 中包含更多的 CUDA 核心,提升了并行计算能力。

    3.2 Tensor 核心

    Ampere 架构中的第三代 Tensor 核心支持稀疏矩阵运算,进一步提升了深度学习训练和推理的效率。Tensor 核心能够高效处理混合精度计算,提供更高的计算吞吐量。

    3.3 RT 核心

    Ampere 架构中的第二代 RT 核心改进了光线追踪的性能,支持更加逼真的图像渲染和实时光影效果。RT 核心能够高效处理复杂的光线追踪计算,显著提升图形处理能力。

    3.4 NVLink 和 PCIe 4.0

    Ampere 架构支持第三代 NVLink 和 PCIe 4.0,提供更高的带宽和更低的延迟。NVLink 允许多个 GPU 之间高速互联,PCIe 4.0 提供更快的 CPU-GPU 数据传输速度。

    3.5 新型内存技术

    Ampere 架构使用 GDDR6X 高带宽显存,提供更高的内存带宽和更低的延迟。这种新型显存能够支持更大规模的数据集和更高效的计算。

    4. GPU应用场景

    NVIDIA GPU 在多个领域有广泛的应用,包括但不限于:

    • 深度学习:训练和推理深度神经网络,加速 AI 模型的开发和部署。
    • 高性能计算(HPC):科学计算、气候模拟、金融建模等需要大规模并行计算的应用。
    • 图形处理:实时渲染、图像处理、视频编码和解码。
    • 游戏:高性能图形渲染、物理模拟和 AI 驱动的游戏功能。
    • 数据中心:加速数据分析、数据库查询和大数据处理。

    【二】轻量级模型部署经验分享

    经典的轻量级模型包括mobilenet系列、MobileDets系列、shufflenet系列、cspnet系列、vovnet系列以及repvgg系列等,以下是一些部署经验:

    1. 低算力设备:比如手机移动端 cpu 硬件,可以考虑使用mobilenetv1(深度可分离卷机架构低 FLOPs)、低 FLOPs 和 低MAC的shuffletnetv2(channel_shuffle 算子在推理框架上可能不支持)。
    2. 专用 asic 硬件设备:比如npu 芯片(地平线 x3/x4 等、海思 3519、安霸cv22 等),目标检测问题可以考虑使用 cspnet 网络(减少重复梯度信息)、repvgg(直连架构部署简单,网络并行度高有利于发挥 GPU 算力,但量化后有精度下降的风险) 。
    3. 英伟达 gpu 硬件:比如 t4 芯片,可以考虑使用 repvgg 网络(类 vgg 卷积架构高并行度能带来高速度、单路架构省显存/内存)。
    4. MobileNet block (深度可分离卷积 block, depthwise separable convolution block)在有加速功能的硬件(比如 NPU 芯片)上比较没有效率。除非芯片厂商做了定制优化来提高深度可分离卷积 block 的计算效率,比如地平线机器人 x3 芯片对深度可分离卷积 block 做了定制优化。

    计算机基础

    Rocky从工业界、应用界、竞赛界以及学术界角度出发,总结沉淀AI行业中需要用到的实用计算机基础知识,不仅能在面试中帮助到我们,还能让我们在日常工作中提高效率

    【一】如何使用Git将AI项目切换到特定分支?

    在Git中,我们可以使用以下命令查看AI项目中有多少分支,并切换到特定的分支。以下是详细的步骤和命令解析:

    1. 查看项目中的所有分支

    要查看本地和远程的所有分支,我们可以使用以下命令:

    查看本地分支

    git branch

    这会列出所有本地分支,当前所在的分支会用 * 标记。

    查看远程分支

    git branch -r

    这会列出所有远程分支。

    查看本地和远程分支

    git branch -a

    这会列出所有本地和远程分支。

    2. 切换到特定分支

    要切换到特定的分支,我们可以使用 git checkoutgit switch 命令。

    使用 git checkout 切换分支

    git checkout <branch-name>

    例如,切换到名为 WeThinkIn 的分支:

    git checkout WeThinkIn

    使用 git switch 切换分支

    git switch <branch-name>

    例如,切换到名为 WeThinkIn 的分支:

    git switch WeThinkIn

    操作示例详解

    假设我们在一个AI项目中,想要查看所有分支并切换到一个名为 WeThinkIn 的分支,以下是具体的操作步骤:

    1. 查看本地分支
    git branch

    输出示例:

    * master
      WeThinkIn
      feature-1
    1. 查看远程分支
    git branch -r

    输出示例:

      origin/HEAD -> origin/master
      origin/WeThinkIn
      origin/feature-1
      origin/master
    1. 查看所有分支
    git branch -a

    输出示例:

    * master
      WeThinkIn
      feature-1
      remotes/origin/HEAD -> origin/master
      remotes/origin/develop
      remotes/origin/feature-1
      remotes/origin/master
    1. 切换到 WeThinkIn 分支
    git checkout WeThinkIn

    或者使用 git switch

    git switch WeThinkIn

    【二】在AI项目中,从云端读取传输信息失败,显示网络不可用,一般可以怎么解决呢?

    当在AI项目中从云端读取或传输信息失败,并显示网络不可用时,可能有多种原因。作为算法工程师,我们不需要知根知底关于网络方面的所有知识,但是我们需要知道常见的问题可能性,这样能够与网络部门的同事更好的沟通,同时知道网络端问题排查优化的相应成本,有利于整体AI项目的成本管理

    以下是一些常见的网络不可用原因和相应的解决方案:

    1. 检查网络连接

    确保服务器或本地机器的网络连接正常。这是最基本的步骤,我们可以通过以下方式检查:

    • ping 命令:检查与目标服务器的连接。
      ping www.WeThinkIn.com
    • curl 命令:尝试从目标 URL 下载文件。
      curl -I https://www.WeThinkIn.com/path/to/image.jpg

    2. 检查防火墙和安全组设置

    如果我们使用的是云服务器(如 AWS、GCP、Azure),确保防火墙或安全组设置允许出站 HTTP/HTTPS 请求。

    • AWS 安全组:在AWS管理控制台中,检查安全组的出站规则,确保允许端口 80(HTTP)和 443(HTTPS)。
    • 本地防火墙:检查本地机器的防火墙设置,确保允许 HTTP/HTTPS 流量。

    3. 检查代理设置

    有时项目组的网络或服务器环境可能需要通过代理访问。检查是否需要设置代理:

    • 配置环境变量
      export http_proxy=http://proxy.WeThinkIn.com:port
      export https_proxy=https://proxy.WeThinkIn.com:port

    4. 检查URL和权限

    我们需要确保提供的URL正确,并且有权限访问该URL:

    • URL是否正确:检查URL是否拼写正确。
    • 权限问题:如果URL需要身份验证,确保我们提供了正确的认证信息。

    5. 重试机制

    网络请求有时会因为临时问题失败,我们可以通过添加重试机制可以提高成功率。可以使用 requests 库的 Retry 机制:

    import requests
    from requests.adapters import HTTPAdapter
    from requests.packages.urllib3.util.retry import Retry

    url = 'https://www.WeThinkIn.com/path/to/image.jpg'

    # 创建一个 session
    session = requests.Session()

    # 定义重试策略
    retries = Retry(total=5, backoff_factor=1, status_forcelist=[500502503504])

    # 将重试策略应用到 HTTPAdapter
    session.mount('http://', HTTPAdapter(max_retries=retries))
    session.mount('https://', HTTPAdapter(max_retries=retries))

    try:
        response = session.get(url, timeout=10)
        response.raise_for_status()  # 如果请求失败,抛出 HTTPError
        with open('image.jpg''wb'as f:
            f.write(response.content)
        print('Image downloaded successfully')
    except requests.exceptions.RequestException as e:
        print(f'Error downloading image: {e}')

    6. 检查云端服务状态

    有时云端服务可能会有临时故障或维护,可以及时联系云服务提供商进行问题排查和解决。

    7. 日志记录和错误处理

    我们可以记录详细的错误日志,有助于诊断问题。确保捕获和记录所有可能的异常:

    import logging

    logging.basicConfig(filename='download.log', level=logging.ERROR)

    try:
        response = session.get(url, timeout=10)
        response.raise_for_status()
        with open('image.jpg''wb'as f:
            f.write(response.content)
        print('Image downloaded successfully')
    except requests.exceptions.RequestException as e:
        logging.error(f'Error downloading image: {e}')
        print(f'Error downloading image: {e}')

    开放性问题

    Rocky从工业界、应用界、竞赛界以及学术界角度出发,思考总结AI行业的一些开放性问题,这些问题不仅能够用于面试官的提问,也可以用作面试者的提问,在面试的最后阶段让面试双方进入更深入的探讨与交流。

    与此同时,这些开放性问题也是贯穿我们职业生涯的本质问题,需要我们持续的思考感悟。这些问题没有标准答案,Rocky相信大家心中都有自己对于AI行业的认知与判断,欢迎大家在留言区分享与评论。

    【一】AI算法工程师如何跨过时代周期?

    Rocky认为这是一个非常有价值的问题,就像从传统深度学习时代进入到AIGC时代一样,我们需要挖掘不同时代的相通特点与通识经验

    【二】AIGC、传统深度学习、自动驾驶这三个方向在落地模式上有哪些异同?

    Rocky认为这是一个非常值得我们持续思考的问题,我们需要从AI行业的商业模式角度切入,来倒推我们的技术逻辑,才能有更多成长

    推荐阅读

    1、加入AIGCmagic社区知识星球

    AIGCmagic社区知识星球不同于市面上其他的AI知识星球,AIGCmagic社区知识星球是国内首个以AIGC全栈技术与商业变现为主线的学习交流平台,涉及AI绘画、AI视频、ChatGPT等大模型、AI多模态、数字人、全行业AIGC赋能等50+应用方向,内部包含海量学习资源、专业问答、前沿资讯、内推招聘、AIGC模型、AIGC数据集和源码等

    那该如何加入星球呢?很简单,我们只需要扫下方的二维码即可。知识星球原价:299元/年,前200名限量活动价,终身优惠只需199元/年。大家只需要扫描下面的星球优惠卷即可享受初始居民的最大优惠:

    2、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

    3、Stable DiffusionV1-V2核心原理,核心基础知识,网络结构,经典应用场景,从0到1搭建使用Stable Diffusion进行AI绘画,从0到1上手使用Stable Diffusion训练自己的AI绘画模型,Stable Diffusion性能优化等全维度解析文章正式发布

    码字不易,欢迎大家多多点赞:

    Stable Diffusion文章地址:https://zhuanlan.zhihu.com/p/632809634

    4、ControlNet核心基础知识,核心网络结构,从0到1使用ControlNet进行AI绘画,从0到1上手构建ControlNet高级应用等全维度解析文章正式发布

    码字不易,欢迎大家多多点赞:

    ControlNet文章地址:https://zhuanlan.zhihu.com/p/660924126

    5、LoRA系列模型核心基础知识,从0到1使用LoRA模型进行AI绘画,从0到1上手训练自己的LoRA模型,LoRA变体模型介绍,优质LoRA推荐等全维度解析文章正式发布

    码字不易,欢迎大家多多点赞:

    LoRA文章地址:https://zhuanlan.zhihu.com/p/639229126

    6、最全面的AIGC面经《手把手教你成为AIGC算法工程师,斩获AIGC算法offer!(2024年版)》文章正式发布

    码字不易,欢迎大家多多点赞:

    AIGC面经文章地址:https://zhuanlan.zhihu.com/p/651076114

    7、10万字大汇总《“三年面试五年模拟”之算法工程师的求职面试“独孤九剑”秘籍》文章正式发布

    码字不易,欢迎大家多多点赞:

    算法工程师三年面试五年模拟文章地址:https://zhuanlan.zhihu.com/p/545374303

    《三年面试五年模拟》github项目地址(希望大家能给个star):https://github.com/WeThinkIn/Interview-for-Algorithm-Engineer

    8、Stable Diffusion WebUI、ComfyUI、Fooocus三大主流AI绘画框架核心知识,从0到1搭建AI绘画框架,从0到1使用AI绘画框架的保姆级教程,深入浅出介绍AI绘画框架的各模块功能,深入浅出介绍AI绘画框架的高阶用法等全维度解析文章正式发布

    码字不易,欢迎大家多多点赞:

    AI绘画框架文章地址:https://zhuanlan.zhihu.com/p/673439761

    9、GAN网络核心基础知识、深入浅出解析GAN在AIGC时代的应用等全维度解析文章正式发布!

    码字不易,欢迎大家多多点赞:

    GAN网络文章地址:https://zhuanlan.zhihu.com/p/663157306

    10、其他

    Rocky将YOLOv1-v7全系列大解析文章也制作成相应的pdf版本,大家可以关注公众号WeThinkIn,并在后台 【精华干货】菜单或者回复关键词“YOLO” 进行取用。

    WeThinkIn
    Rocky相信人工智能,数据科学,商业逻辑,金融工具,终身成长,以及顺应时代的潮流会赋予我们超能力。
     最新文章