大家好,我是賀老師,嵌入式 AI 工程師,《嵌入式AI:讓單片機(jī)學(xué)會(huì)思考》主理人,專注AI在MCU上的落地實(shí)踐。

很多嵌入式工程師開始接觸 AI 時(shí),最容易走偏的地方,不是代碼,而是神經(jīng)網(wǎng)絡(luò)基礎(chǔ)沒真正建立起來。表面上會(huì)調(diào)用訓(xùn)練腳本、會(huì)導(dǎo)出 .tflite,但一問模型為什么這么設(shè)計(jì)、輸入輸出到底代表什么、為什么量化后精度會(huì)掉,就說不清楚。
做嵌入式 AI,不需要一開始就去研究很深的數(shù)學(xué)推導(dǎo),但有幾類神經(jīng)網(wǎng)絡(luò)知識(shí)必須真正掌握。因?yàn)檫@些知識(shí)會(huì)直接決定你能不能看懂模型結(jié)構(gòu)、能不能判斷模型是否適合 MCU、能不能定位訓(xùn)練和部署中的問題。
下面不講空泛概念,直接講最重要、最常用、最應(yīng)該掌握的內(nèi)容。
神經(jīng)網(wǎng)絡(luò)本質(zhì)上是在學(xué)習(xí)“輸入和輸出之間的映射關(guān)系”。所以第一步,不是先看層數(shù),而是先搞清楚輸入和輸出分別是什么。
例如動(dòng)作識(shí)別中,一條樣本可以是“連續(xù) 128 個(gè)時(shí)刻的三軸加速度數(shù)據(jù)”;特征就是這 128×3 個(gè)數(shù)字;標(biāo)簽可能是“走路、跑步、靜止”中的某一類。
# 一個(gè)動(dòng)作識(shí)別樣本的概念示意
sample.shape = (128, 3)
# 128 表示時(shí)間步
# 3 表示 ax, ay, az 三個(gè)通道
label = 1 # 例如 0=靜止, 1=走路, 2=跑步如果這一步?jīng)]搞清楚,后面訓(xùn)練、量化、部署都會(huì)錯(cuò)。因?yàn)槟P筒皇窃趯W(xué)習(xí)“抽象概念”,而是在學(xué)習(xí)這堆數(shù)字和目標(biāo)之間的關(guān)系。
張量可以先簡單理解成“帶形狀信息的多維數(shù)組”。嵌入式工程師可以先不用把它想得太復(fù)雜,本質(zhì)上就是數(shù)據(jù)加說明書。
最需要關(guān)注的是 shape,也就是數(shù)據(jù)形狀。因?yàn)?shape 一錯(cuò),模型立刻無法正常工作。
# 常見輸入例子
(1, 128) # 單條一維序列
(1, 128, 3) # 單條時(shí)間序列,多通道輸入
(1, 32, 32, 1) # 單張灰度圖
(1, 40) # 單條提取后的特征向量這里的第一個(gè)維度 1 常常表示 batch size,也就是一次送進(jìn)去幾條樣本。部署到 MCU 上時(shí),通常就是一次處理一條,所以常見是 1。
最容易犯的錯(cuò)誤:訓(xùn)練時(shí)輸入是 (128, 3),部署時(shí)卻按 (3, 128) 去填數(shù)據(jù);訓(xùn)練時(shí)做了歸一化,部署時(shí)直接送原始值;訓(xùn)練時(shí)輸入是 float,部署時(shí)卻按 int8 生塞進(jìn)去。
把神經(jīng)網(wǎng)絡(luò)拆到最小,你會(huì)發(fā)現(xiàn)每個(gè)神經(jīng)元做的事情很簡單:先做加權(quán)求和,再加偏置,最后過一個(gè)激活函數(shù)。
y = activation(w1*x1 + w2*x2 + ... + wn*xn + b)這里:
x1, x2 ...w1, w2 ...bactivation()你可以把權(quán)重理解成“每個(gè)輸入有多重要”,把偏置理解成“整體往哪個(gè)方向再推一把”。
訓(xùn)練神經(jīng)網(wǎng)絡(luò),本質(zhì)上就是不斷調(diào)整權(quán)重和偏置,讓模型輸出更接近真實(shí)標(biāo)簽。也就是說,訓(xùn)練出來的模型,最后真正被部署到 MCU 上的核心資產(chǎn),其實(shí)就是一堆參數(shù)。
# 以一層全連接層為例
# 輸入長度: 10
# 輸出長度: 4
weights.shape = (10, 4)
bias.shape = (4, )
# 參數(shù)個(gè)數(shù) = 10*4 + 4 = 44這里你就要立刻建立一個(gè)意識(shí):參數(shù)個(gè)數(shù)越多,模型一般越大,占用 Flash 越多,推理時(shí)乘加運(yùn)算也越多。
如果沒有激活函數(shù),多層全連接堆起來本質(zhì)上還是線性變換,表達(dá)能力很有限。激活函數(shù)的作用,就是讓模型能擬合更復(fù)雜的關(guān)系。
非常實(shí)用的判斷方式:隱藏層最常見看 ReLU,二分類輸出常見看 Sigmoid,多分類輸出常見看 Softmax。如果你在 Netron 里看到這些結(jié)構(gòu),至少應(yīng)該知道它大概在干什么。
全連接層的意思是:上一層每個(gè)輸入都和下一層每個(gè)輸出相連。
# 假設(shè)輸入長度 128,輸出長度 64
參數(shù)個(gè)數(shù) = 128 * 64 + 64 = 8256它的優(yōu)點(diǎn)是簡單直接,適合做小型分類或回歸;缺點(diǎn)是輸入一大,參數(shù)就會(huì)迅速增多。所以在 MCU 上,全連接層可以用,但不能無限堆大。
卷積層的核心不是“看上去高級(jí)”,而是它用較少參數(shù)去提取局部模式。做圖像、音頻頻譜、時(shí)序局部模式識(shí)別時(shí)都很常見。
例如關(guān)鍵詞識(shí)別里,輸入如果是聲學(xué)特征圖,卷積層就可以提取局部時(shí)間-頻率模式;動(dòng)作識(shí)別里,如果把連續(xù)數(shù)據(jù)組織得合適,一維卷積也常見。
# 粗略理解
# 全連接:每個(gè)輸入都連每個(gè)輸出
# 卷積:用一個(gè)小窗口在輸入上滑動(dòng)提特征對(duì)嵌入式來說,卷積層的關(guān)鍵不只是“能不能跑”,還要看卷積核大小、通道數(shù)、輸出特征圖尺寸,否則 RAM 壓力會(huì)很大。
很多部署問題,并不是主干層不會(huì)看,而是這些“輔助層”沒搞清楚,導(dǎo)致輸入輸出 shape 對(duì)不上。
所以你看到模型最后一層,不能只會(huì)說“這是輸出層”,而應(yīng)該能判斷:這是分類還是回歸,輸出值是不是概率,后處理到底應(yīng)該取最大值還是直接讀數(shù)。
損失函數(shù)不是理論擺設(shè),它就是訓(xùn)練過程中的“誤差尺子”。模型每訓(xùn)練一次,都會(huì)看看當(dāng)前輸出和真實(shí)標(biāo)簽差多少,然后根據(jù)這個(gè)誤差去調(diào)整參數(shù)。
反向傳播的本質(zhì),就是把輸出誤差一層一層往前傳,然后告訴每個(gè)參數(shù)該往哪個(gè)方向調(diào),調(diào)多少。你可以先不推導(dǎo)梯度公式,但必須知道:訓(xùn)練階段和推理階段是兩件完全不同的事。
這也是為什么 MCU 上通常只做推理,不做訓(xùn)練。訓(xùn)練太耗算力、太耗內(nèi)存、太耗時(shí)間。
嵌入式AI里,很多人只盯著訓(xùn)練集準(zhǔn)確率,這是不夠的。你要看訓(xùn)練集和驗(yàn)證集的表現(xiàn)是否一致。
# 一個(gè)典型過擬合現(xiàn)象
epoch 1: train_acc = 0.72, val_acc = 0.69
epoch 10: train_acc = 0.96, val_acc = 0.75
epoch 20: train_acc = 0.99, val_acc = 0.71如果出現(xiàn)這種情況,繼續(xù)訓(xùn)練往往沒有意義,應(yīng)該去檢查數(shù)據(jù)量、數(shù)據(jù)分布、模型大小、正則化、數(shù)據(jù)增強(qiáng)這些問題。
尤其是異常檢測、故障識(shí)別這類任務(wù),只看準(zhǔn)確率很容易誤判。
非常實(shí)用的一句話:如果你的任務(wù)里“漏掉異?!北取岸鄨?bào)幾個(gè)異常”更嚴(yán)重,那你就不能只盯準(zhǔn)確率,要重點(diǎn)看召回率。
在 PC 上訓(xùn)練時(shí),你可能默認(rèn)用的是 float32。每個(gè)參數(shù) 4 字節(jié),算起來方便,精度也高。但一到 MCU 上,F(xiàn)lash、RAM、算力都很緊,所以常見做法是量化成 int8。
如果一個(gè)模型有 20,000 個(gè)參數(shù):
float32 大小約 = 20000 * 4 = 80000 字節(jié) ≈ 78 KB
int8 大小約 = 20000 * 1 = 20000 字節(jié) ≈ 19.5 KB差距非常直接。所以做嵌入式AI,必須具備“看到模型結(jié)構(gòu)就開始估算資源”的習(xí)慣。
量化的本質(zhì),是用更低精度的整數(shù)去近似原來的浮點(diǎn)數(shù)。近似就一定會(huì)帶來誤差。
real_value ≈ scale * (quantized_value - zero_point)你至少要知道三個(gè)點(diǎn):
很多人以為模型能塞進(jìn) Flash 就能跑,這是不對(duì)的。除了參數(shù)本身,推理時(shí)還有中間激活值、輸入輸出緩沖區(qū)、運(yùn)行時(shí)工作區(qū)。
所以做嵌入式部署時(shí),至少要同時(shí)看四件事:
當(dāng)你拿到一個(gè)神經(jīng)網(wǎng)絡(luò)模型,不要先問“這模型先進(jìn)不先進(jìn)”,而要先按下面順序判斷:
你如果能按這六步去看模型,已經(jīng)比只會(huì)“跑腳本”的狀態(tài)強(qiáng)很多了。
學(xué)習(xí)嵌入式AI,神經(jīng)網(wǎng)絡(luò)知識(shí)不用一開始鋪得特別寬,但一定要學(xué)得足夠扎實(shí)。最應(yīng)該優(yōu)先掌握的,不是復(fù)雜理論,而是下面這些真正會(huì)影響部署的核心內(nèi)容:
把這些真正學(xué)明白,你再去看訓(xùn)練腳本、模型結(jié)構(gòu)圖、推理代碼,很多原來模糊的地方都會(huì)變清楚。到那時(shí),你就不是“在調(diào)用 AI 工具”,而是真的開始理解神經(jīng)網(wǎng)絡(luò)在嵌入式設(shè)備上是怎么工作的。

END