本發(fā)明屬于神經(jīng)網(wǎng)絡(luò),特別涉及一種基于simd指令的lstm實(shí)現(xiàn)方法。
背景技術(shù):
1、在傳統(tǒng)神經(jīng)網(wǎng)絡(luò)中,模型不會(huì)關(guān)注上一時(shí)刻的處理會(huì)有什么信息可以用于下一時(shí)刻,每一次都只會(huì)關(guān)注當(dāng)前時(shí)刻的處理。舉個(gè)例子來說,我們想對(duì)一部影片中每一刻出現(xiàn)的事件進(jìn)行分類,如果我們知道電影前面的事件信息,那么對(duì)當(dāng)前時(shí)刻事件的分類就會(huì)非常容易。實(shí)際上,傳統(tǒng)神經(jīng)網(wǎng)絡(luò)沒有記憶功能,所以它對(duì)每一刻出現(xiàn)的事件進(jìn)行分類時(shí)不會(huì)用到影片已經(jīng)出現(xiàn)的信息,而recurrent?neural?networks(rnns)遞歸神經(jīng)網(wǎng)絡(luò)可以讓神經(jīng)網(wǎng)絡(luò)能夠記住這些信息,遞歸神經(jīng)網(wǎng)絡(luò)的結(jié)果與傳統(tǒng)神經(jīng)網(wǎng)絡(luò)有一些不同,它帶有一個(gè)指向自身的環(huán),用來表示它可以傳遞當(dāng)前時(shí)刻處理的信息給下一時(shí)刻使用。遞歸神經(jīng)網(wǎng)絡(luò)因?yàn)榫哂幸欢ǖ挠洃浌δ?,可以被用來解決很多問題,例如:語音識(shí)別、語言模型、機(jī)器翻譯等。但是它并不能很好地處理長(zhǎng)時(shí)依賴問題。
2、長(zhǎng)時(shí)依賴問題指當(dāng)預(yù)測(cè)點(diǎn)與依賴的相關(guān)信息距離比較遠(yuǎn)的時(shí)候,就難以學(xué)到該相關(guān)信息。例如在句子“我出生在法國(guó),……,我會(huì)說法語”中,若要預(yù)測(cè)末尾“法語”,我們需要用到上下文“法國(guó)”。理論上,遞歸神經(jīng)網(wǎng)絡(luò)是可以處理這樣的問題的,但是實(shí)際上,rnn存在的問題是梯度消失和梯度爆炸。由于梯度消失只有短時(shí)記憶,無法解決長(zhǎng)期依賴問題??梢酝ㄟ^選取更好的激活函數(shù)或改變網(wǎng)絡(luò)結(jié)構(gòu)(比如lstm)來改善梯度消失的問題。lstm通過精妙的門控制將加法運(yùn)算帶入網(wǎng)絡(luò)中,一定程度上解決了梯度消失的問題。
3、lstm的核心如圖1所示,被稱為memory?block(記憶塊),主要包含了三個(gè)門(遺忘門forget?gate、輸入門input?gate、輸出門output?gate)與一個(gè)記憶單元cell。下圖中心的c?t即cell,從下方輸入(h?t-1,x?t)到輸出h?t的一條線即為cell?state(單元狀態(tài)),它就像一個(gè)傳送帶,可以控制信息傳遞給下一時(shí)刻。c_in?t是c?t的候選輸入,是由tanh層生成的候選值。f?t,it,ot分別為遺忘門、輸入門、輸出門,用sigmoid層表示,門的輸出是一個(gè)介于0到1的數(shù),表示允許信息通過的多少,0表示完全不允許通過,1表示允許完全通過。lstm通過門控單元可以對(duì)cell添加和刪除信息,通過門可以有選擇地決定信息是否通過。
4、lstm第一步是由“forget?gate”層通過sigmoid來控制,它會(huì)根據(jù)上一時(shí)刻的輸出h?t-1和當(dāng)前輸入x?t來產(chǎn)生一個(gè)0到1的f?t值,來決定是否讓上一時(shí)刻學(xué)到的信息c?t-1通過或部分通過。舉個(gè)例子來說就是,在之前的句子中學(xué)到了很多東西,一些東西對(duì)當(dāng)前來講是沒用的,可以對(duì)它進(jìn)行選擇性地過濾。
5、lstm第二步包含兩部分,第一個(gè)是由“input?gate”層通過sigmoid來決定哪些值用來更新,第二個(gè)是一個(gè)tanh層用來生成新的候選值c_in?t,它作為當(dāng)前層產(chǎn)生的候選值可能會(huì)添加到cell?state中。
6、一二步結(jié)合起來對(duì)舊的cell?state進(jìn)行更新,首先將舊的cell?state乘以f?t來忘掉不需要的信息,然后再與it*c_in?t的結(jié)果相加,得到了新的cell?state即c?t。這就是丟掉不需要的信息,添加新信息的過程。
7、lstm最后一步是決定模型的輸出,首先是通過sigmoid層來得到一個(gè)初始輸出,然后使用tanh將c?t值縮放到-1到1間,再與初始輸出逐對(duì)相乘,從而得到模型的輸出。如圖1所示。
8、目前在深度學(xué)習(xí)框架pytorch和tensorflow中都有構(gòu)建lstm的接口。
9、現(xiàn)有深度學(xué)習(xí)框架中的lstm都是基于python語言實(shí)現(xiàn)的,在pc端可以運(yùn)行,但是對(duì)于一些芯片廠商用于運(yùn)行深度學(xué)習(xí)神經(jīng)網(wǎng)絡(luò)的芯片,例如北京君正集成電路股份有限公司(簡(jiǎn)稱:北京君正)的芯片,python語言實(shí)現(xiàn)的算法是無法運(yùn)行的。另外,python的底層語言是c語言,在資源有限的平臺(tái)或芯片上運(yùn)行c語言會(huì)造成大量緩存浪費(fèi)從而導(dǎo)致運(yùn)算速度慢,對(duì)于一些要求運(yùn)算速度的應(yīng)用場(chǎng)合,python實(shí)現(xiàn)的算法也不是最佳選擇。
10、此外,常用的技術(shù)術(shù)語包括:
11、1.simd全稱single?instruction?multiple?data,單指令多數(shù)據(jù)流,能夠一次處理多個(gè)操作數(shù),并把它們打包在大型寄存器的一組指令集。
12、2.rnn全稱recurrent?neural?network,遞歸神經(jīng)網(wǎng)絡(luò),rnn是專門處理序列數(shù)據(jù)的網(wǎng)絡(luò),是對(duì)序列數(shù)據(jù)最自然的神經(jīng)網(wǎng)絡(luò)架構(gòu),在語音識(shí)別、語言翻譯和圖片描述等方面獲得應(yīng)用。
13、3.lstm全稱long?short?term?memory?network,長(zhǎng)短期記憶神經(jīng)網(wǎng)絡(luò),是一種改進(jìn)之后的遞歸神經(jīng)網(wǎng)絡(luò),可以解決rnn無法處理長(zhǎng)距離的依賴的問題,目前比較流行。
技術(shù)實(shí)現(xiàn)思路
1、為了解決上述問題,本技術(shù)的目的在于:提出一種基于simd指令集的lstm的實(shí)現(xiàn)方法,該方法通過將simd指令組合使用完成與python實(shí)現(xiàn)的lstm相同的功能,可以在特定芯片(例如北京君正用于運(yùn)行深度學(xué)習(xí)神經(jīng)網(wǎng)絡(luò)的芯片)上高效運(yùn)行l(wèi)stm算法。
2、具體地,本發(fā)明提供一種基于simd指令的lstm實(shí)現(xiàn)方法,所述方法包括步驟如下:
3、s1,執(zhí)行第一層循環(huán)是時(shí)間序列的循環(huán),每次對(duì)一個(gè)時(shí)間序列的值進(jìn)行計(jì)算得出結(jié)果,一共循環(huán)time_sequence次,表示為:
4、for(int?t=0;t<time_sequence;t++);
5、s2,執(zhí)行第二層循環(huán)是輸出通道的循環(huán),輸出通道有output_channel個(gè),由于數(shù)據(jù)類型是int8,ingenic_simd128指令集一次處理128bit數(shù)據(jù),所以一次能處理16個(gè)int8數(shù)據(jù),一共是output_channel/16次循環(huán);表示為:for(int?j=0;j<=output_channel-16;j+=16);
6、每次循環(huán)先計(jì)算4種權(quán)重的數(shù)據(jù)指針,表示為:
7、int8_t*weight_i_ptr=weight_i+j*ic;weight_i是權(quán)重i的起始指針,每個(gè)輸出通道對(duì)應(yīng)ic個(gè)weight,當(dāng)輸出通道循環(huán)到j(luò)時(shí),對(duì)應(yīng)j*ic個(gè)weight,所以本次循環(huán)的weight指針為起始指針weight_i加上j*ic;同理得到,int8_t*weight_j_ptr=weight_j+j*ic;
8、int8_t*weight_f_ptr=weight_f+j*ic;
9、int8_t*weight_o_ptr=weight_o+j*ic;
10、每次循環(huán)需要把sum_i,sum_j,sum_f,sum_o重新置0,表示為:sum_i=0;
11、sum_j=0;
12、sum_f=0;
13、sum_o=0;
14、s3,第三層循環(huán)分成兩個(gè)循環(huán),因?yàn)橛袃蓚€(gè)輸入數(shù)據(jù)input_data和input_hidden;
15、第一個(gè)循環(huán)是對(duì)input_data數(shù)據(jù)循環(huán),同理一次處理16個(gè)int8數(shù)據(jù),有input_channel/16次循環(huán),表示為:
16、for(int?k=0;k<=input_channel-16;k+=16);
17、使用ingenic_simd128_load數(shù)據(jù)加載指令,一次加載16個(gè)input_data數(shù)據(jù)到寄存器in中,表示為:in=ingenic_simd128_load(input_data);
18、使用ingenic_simd128_fwsum乘加指令,從weight_i_ptr中加載16個(gè)weight數(shù)據(jù),與in中的16個(gè)輸入數(shù)據(jù)對(duì)應(yīng)相乘后再與sum_i中的16個(gè)數(shù)據(jù)對(duì)應(yīng)相加,sum_i是每次循環(huán)后的累加值,所以第一個(gè)循環(huán)結(jié)束后sum_i中是16個(gè)輸出通道對(duì)input_data計(jì)算得到的sum值,表示為:sum_i=ingenic_simd128_fwsum(sum_i,in,weight_i_ptr);
19、同理得到此16個(gè)輸出通道對(duì)應(yīng)的sum_j,sum_f,sum_o值,表示為:sum_j=ingenic_simd128_fwsum(sum_j,in,weight_j_ptr);
20、sum_f=ingenic_simd128_fwsum(sum_f,in,weight_f_ptr);
21、sum_o=ingenic_simd128_fwsum(sum_o,in,weight_o_ptr);
22、第二個(gè)循環(huán)是對(duì)input_hidden數(shù)據(jù)循環(huán),同理一次處理16個(gè)int8數(shù)據(jù),有hidden_channel/16次循環(huán),表示為:
23、for(int?k=0;k<=hidden_channel-16;k+=16);
24、使用ingenic_simd128_load指令將input_hidden數(shù)據(jù)加載到in_h寄存器中,表示為:
25、in_h=ingenic_simd128_load(input_hidden);
26、使用ingenic_simd128_fwsum指令將weight與input_hidden數(shù)據(jù)相乘后再與sum值相加,這里的sum值是上一個(gè)input_data循環(huán)計(jì)算得到的sum值;第二個(gè)循環(huán)結(jié)束后sum_i中是16個(gè)輸出通道對(duì)input_data和input_hidden計(jì)算得到的累加值,表示為:
27、sum_i=ingenic_simd128_fwsum(sum_i,in_h,weight_i_ptr);
28、同理可得到此16個(gè)輸出通道對(duì)應(yīng)的sum_j,sum_f,sum_o值,表示為:sum_j=ingenic_simd128_fwsum(sum_j,in_h,weight_j_ptr);
29、sum_f=ingenic_simd128_fwsum(sum_f,in_h,weight_f_ptr);
30、sum_o=ingenic_simd128_fwsum(sum_o,in_h,weight_o_ptr);
31、s4,第三層循環(huán)結(jié)束后在第二層循環(huán)中對(duì)sum_i,sum_j,sum_f,sum_o繼續(xù)進(jìn)行計(jì)算;
32、使用ingenic_simd128_sigmoid指令其為singmoid指令,對(duì)寄存器sum_i中的16個(gè)sum值進(jìn)行sigmoid計(jì)算,將得到的結(jié)果存儲(chǔ)在quant_i寄存器中,表示為:
33、quant_i=ingenic_simd128_sigmoid(sum_i);
34、同理得quant_j,quant_f,quant_o,表示為:
35、quant_j=ingenic_simd128_sigmoid(sum_j);
36、quant_f=ingenic_simd128_sigmoid(sum_f);
37、quant_o=ingenic_simd128_sigmoid(sum_o);
38、加載16個(gè)input_cell數(shù)據(jù)到寄存器in_c中,表示為:
39、in_c=ingenic_simd128_load(input_cell);
40、使用ingenic_simd128_mul乘法指令,將quant_f中的16個(gè)數(shù)與in_c中的16個(gè)數(shù)對(duì)應(yīng)相乘,結(jié)果保存在寄存器fc中,表示為:fc=ingenic_simd128_mul(quant_f,in_c);
41、使用ingenic_simd128_tanh指令其是tanh指令,對(duì)quant_j中的16個(gè)值進(jìn)行tanh計(jì)算,得到的結(jié)果保存在寄存器act_j中,表示為:act_j=ingenic_simd128_tanh(quant_j);
42、quant_i和quant_j的值對(duì)應(yīng)相乘結(jié)果保存在寄存器ij中,表示為:ij=ingenic_simd128_mul(quant_i,quant_j);
43、使用ingenic_simd128_add加法指令,將fc中的16個(gè)數(shù)與ij中的16個(gè)數(shù)對(duì)應(yīng)相加,結(jié)果保存在寄存器new_c中,表示為:
44、new_c=ingenic_simd128_add(fc,ij);
45、對(duì)new_c中的16個(gè)值進(jìn)行tanh計(jì)算,得到的結(jié)果保存在寄存器act_new_c中,表示為:
46、act_new_c=ingenic_simd128_tanh(new_c);
47、quant_o和act_new_c的值對(duì)應(yīng)相乘結(jié)果保存在寄存器new_h中,表示為:
48、new_h=ingenic_simd128_mul(quant_o,act_new_c);
49、使用ingenic_simd128_store數(shù)據(jù)存儲(chǔ)指令,將寄存器new_h里面的16個(gè)數(shù)據(jù)保存到輸出數(shù)據(jù)指針out_ptr中,表示為:
50、out_ptr=ingenic_simd128_store(new_h);
51、至此,第二層循環(huán)結(jié)束,已經(jīng)得到當(dāng)前時(shí)間序列的所有輸出output_channel的信息,再進(jìn)行下一次時(shí)間序列的循環(huán)即步驟s1第一層循環(huán),直至第一層循環(huán)結(jié)束。
52、所述方法使用的simd指令集為ingenic_simd128指令集,該指令集能夠在一個(gè)寄存器中一次加載128bit的數(shù)據(jù),在計(jì)算時(shí)能夠一次對(duì)128bit數(shù)據(jù)進(jìn)行相同操作,該指令集包括數(shù)據(jù)加載、數(shù)據(jù)存儲(chǔ)指令,算術(shù)運(yùn)算加、減、乘指令,邏輯運(yùn)算與、或、非、異或指令,移位運(yùn)算邏輯左移、邏輯右移、算術(shù)右移指令。
53、所述方法中time_sequence表示共有time_sequence個(gè)時(shí)間序列,即需要計(jì)算time_sequence個(gè)時(shí)刻的特征信息;
54、每個(gè)序列輸出通道數(shù)為output_channel,即每個(gè)序列需要輸出output_channel個(gè)特征信息;
55、每個(gè)序列輸入通道數(shù)為input_channel,即每個(gè)序列輸入input_channel個(gè)特征信息,輸入數(shù)據(jù)input_data類型為有符號(hào)8bit即int8_t;
56、上一個(gè)序列的h值也作為本序列的輸入被稱為隱藏層,隱藏層的通道為hidden_channel,表示有hidden_channel個(gè)特征信息,所以每個(gè)序列的輸入有兩部分構(gòu)成,包括輸入數(shù)據(jù)input_data和隱藏層輸入數(shù)據(jù)input_hidden,總的輸入通道數(shù)ic=input_channel+hidden_channel;
57、每個(gè)輸出通道對(duì)應(yīng)ic個(gè)權(quán)重;需要三個(gè)門ft,it,ot和一個(gè)候選輸入c_int參與計(jì)算得到輸出結(jié)果;weight_f,weight_i,weight_o分別對(duì)應(yīng)三個(gè)門的權(quán)重,weight_j對(duì)應(yīng)c_int的權(quán)重,weight_i_ptr,weight_j_ptr,weight_f_ptr,weight_o_ptr是4種權(quán)重對(duì)應(yīng)的數(shù)據(jù)指針;
58、sum_i,sum_j,sum_f,sum_o是輸入數(shù)據(jù)與對(duì)應(yīng)權(quán)重相乘后的值;
59、quant_i,quant_j,quant_f,quant_o是對(duì)4個(gè)sum值進(jìn)行sigmoid計(jì)算后的值,通過sigmoid計(jì)算后得到0~1的值,表示保留信息多少;
60、上一個(gè)時(shí)間序列的cell值也作為本序列的另外一個(gè)輸入即input_cell;fc表示遺忘門forget?gate對(duì)input_cell保留的信息;將quant_j進(jìn)行一次tanh再與quant_i相乘,得到新的權(quán)重ij;權(quán)重ij與保留的信息fc相乘是對(duì)信息再進(jìn)行一次選擇,得到新的cell即new_c;對(duì)新的cell進(jìn)行tanh運(yùn)算再與quant_o相乘得到新的h數(shù)據(jù)即new_h,這里的權(quán)重ij是一個(gè)變量名字,是quant_j與quant_i相乘得到的權(quán)重ij。
61、由此,本技術(shù)的優(yōu)勢(shì)在于:
62、1.使用simd指令集,特別是采用北京君正研發(fā)的芯片中的ingenic_simd128指令集,實(shí)現(xiàn)了lstm算法,可以在使用ingenic_simd128指令集的北京君正t31芯片上運(yùn)行l(wèi)stm算法,與python實(shí)現(xiàn)的lstm算法結(jié)果一致。
63、2.指令集特別是ingenic_simd128指令集一次可以加載128bit數(shù)據(jù),一條指令可以對(duì)128bit的數(shù)據(jù)同時(shí)進(jìn)行計(jì)算,大大提高了計(jì)算速度,在硬件資源有限并且要求計(jì)算速度的芯片上可以高效運(yùn)行,經(jīng)對(duì)比,在相同芯片條件下,simd實(shí)現(xiàn)的lstm算法比python實(shí)現(xiàn)的lstm算法快5倍。