環境資訊中心綜合外電;姜唯 編譯;林大利 審校
本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※回頭車貨運收費標準
※產品缺大量曝光嗎?你需要的是一流包裝設計!
※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面
※推薦評價好的iphone維修中心
※教你寫出一流的銷售文案?
※台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!
※台中搬家遵守搬運三大原則,讓您的家具不再被破壞!
居家、公司行號垃圾清運、廢棄物處理、大型家具回收,服務快速,包月及計重或計桶供客戶選擇,合法登記的清潔公司、廢棄物清除許可,專業技術人員及專業廢棄物清運車輛
項目上線之後,如果是後端報錯,只能重新編譯打包部署然後重啟;如果僅僅是前端頁面、樣式、腳本修改,只需要替換到就可以了。
小公司的話可能比較自由,可以隨意替換,但是有些公司權限設置的比較嚴格,需要提交申請交給運維去處理。
如果僅僅是一個前端問題,又很緊急,這時候提申請走流程勢必會影響到用戶的正常使用。
今天,擼主給大家推薦一款前端代碼文件編輯器來解決以上問題。
定義實體,用於前端文件樹展示:
@Data
public class SysFile {
private Integer fileId;
private String name;
private Integer parentId;
private String parentPath;
}
由於項目採用的是SpringBoot框架,打成了war包部署,後端採用以下方式獲取文件列表:
/**
* 列表
* @return
*/
@RequestMapping(value = "list", method = RequestMethod.POST)
public Result list() throws FileNotFoundException {
String filePath = ResourceUtils.getURL("classpath:").getPath();
List<SysFile> fileList = new ArrayList<>();
getAllFilePaths(filePath,fileList,0,"");
return Result.ok(fileList);
}
遞歸獲取某目錄下的所有子目錄以及子文件:
/**
* 遞歸獲取某目錄下的所有子目錄以及子文件
* @param filePath
* @param filePathList
* @return
*/
private static List<SysFile> getAllFilePaths(String filePath, List<SysFile> filePathList,
Integer level,String parentPath) {
File[] files = new File(filePath).listFiles();
if (files == null) {
return filePathList;
}
for (File file : files) {
int num = filePathList.size()+1;
SysFile sysFile = new SysFile();
sysFile.setName(file.getName());
sysFile.setFileId(num);
sysFile.setParentId(level);
if (file.isDirectory()) {
if(level==0){
if(file.getName().equals("templates")||
file.getName().equals("static")){
filePathList.add(sysFile);
parentPath = SystemConstant.SF_FILE_SEPARATOR+file.getName();
getAllFilePaths(file.getAbsolutePath(), filePathList,num,parentPath);
num++;
}
}else{
filePathList.add(sysFile);
String subParentPath = parentPath+SystemConstant.SF_FILE_SEPARATOR+file.getName();
getAllFilePaths(file.getAbsolutePath(), filePathList,num,subParentPath);
num++;
}
} else {
if(level!=0){
sysFile.setParentPath(parentPath+SystemConstant.SF_FILE_SEPARATOR+file.getName());
filePathList.add(sysFile);
num++;
}
}
}
return filePathList;
}
獲取文件內容:
/**
* 獲取內容
* @return
*/
@RequestMapping(value = "getContent", method = RequestMethod.POST)
public Result getContent(String filePath) throws FileNotFoundException {
String path = ResourceUtils.getURL("classpath:").getPath();
String content = FileUtil.readUtf8String(path+filePath);
return Result.ok(content);
}
修改保存:
/**
* 保存內容
* @return
*/
@RequestMapping(value = "save", method = RequestMethod.POST)
public Result save(String filePath, String content) throws FileNotFoundException {
String path = ResourceUtils.getURL("classpath:").getPath();
/**
* 生產環境自行解除
*/
if(active.equals("prod")){
return Result.error("演示環境禁止插插插!!!");
}else{
File file = new File(path+filePath);
long lastModified = file.lastModified();
FileUtil.writeUtf8String(content,path+filePath);
file.setLastModified(lastModified);
return Result.ok();
}
}
當然了,如果代碼修改比較多,也可以對文件進行上傳覆蓋操作。
如果身邊恰好沒有工具連接遠程服務,亦或是自己沒有服務器的權限,這款在線修改器,擼主覺得還是很方便的。但一定要控制好權限,防止普通人員亂修改,還有一定要做好安全日誌記錄。
https://gitee.com/52itstyle/SPTools
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線
※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整
※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!
※教你寫出一流的銷售文案?
※超省錢租車方案
※回頭車貨運收費標準
摘錄自2020年5月19日自由時報報導
南韓LG化學再傳工安意外,位在忠清南道瑞山市的催化劑工廠,在當地時間今(19)日下午2時20分(台灣時間下午1時20分)左右爆炸失火,造成1死2傷。
《韓聯社》報導,該化工廠的催化劑疑似在過高的壓力下爆炸而引發大火,火勢已在下午3時30分(台灣時間下午2時30分)被撲滅,據消防部門表示,沒有有害化學物質外洩。目前該設施已關閉,警方和消防部門將在清理現場後調查確切事故原因。
本月7日,LG化學在印度投資的一家化工廠發生重大事故,廠內兩座5000公噸級苯乙烯儲存槽因不明原因發生嚴重外洩,造成12人死亡,1000多人住院。
建築
公害污染
生活環境
污染治理
國際新聞
南韓
化學工廠
工安事故
化工廠爆炸
本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※產品缺大量曝光嗎?你需要的是一流包裝設計!
※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面
※回頭車貨運收費標準
※推薦評價好的iphone維修中心
※超省錢租車方案
※台中搬家遵守搬運三大原則,讓您的家具不再被破壞!
※推薦台中搬家公司優質服務,可到府估價
摘錄自2020年5月19日自由時報報導
美國國防部已對國會提出一項提案,擴大對稀土的投資上限,以停止對中國的依賴程度,這些稀土可以用來製造飛彈和彈藥、極音速武器,及相關電子產品。如果美國可以重新生產稀土,中國打「稀土牌」的威脅程度將大幅降低。
根據《國防新聞》報導,美國國防部希望提高《國防生產法》的支出上限,在開採稀土上提升至最高17.5億美元(約新台幣523億),在微電子晶片上增至3.5億美元(約新台幣104億),當涉及到極音速武器時,將會沒有上限。據悉,此提案已於本月初提出,已納入國會正在起草的年度國防政策法案。
美國防部副部長洛德(Ellen Lord)去年8月曾表示,國防部正與澳洲進行談判,要求其為美軍提供稀土。澳洲Lynas公司擁有稀土礦,同時在馬來西亞也有精煉廠,可能是此計劃的核心。
本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※回頭車貨運收費標準
※產品缺大量曝光嗎?你需要的是一流包裝設計!
※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面
※推薦評價好的iphone維修中心
※教你寫出一流的銷售文案?
※台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!
※台中搬家遵守搬運三大原則,讓您的家具不再被破壞!
但本田擅長的空間攫取能力則得以比較充分的體現,儘管XR-V的車身三圍尺寸並不算大,但在乘坐空間的表現上,好感度要比同級別的昂科拉更勝一籌。動力:非常經典的對決別克昂科拉與本田XR-V的對比也可以說是很多人十分在意的“渦輪增壓與自然吸氣的對比”,別克昂科拉採用1。
別克昂科拉 VS 本田XR-V?
當下汽車設計的主流也跟汽車消費的主流趨勢一樣,越來越趨於年輕化,別克昂科拉和東風本田XR-V則都是設計偏向年輕時尚的小型SUV,也是很多首次購車人群在合資小型SUV這個細分市場的首要考慮對象車型,那麼這兩款車型各自優勢在哪?又該如何選擇?
由於兩車在指導價格方面差距還是比較大,別克昂科拉明顯偏貴,而且別克昂科拉的細分車型當中有一款四驅配置車型,反觀XR-V全系沒有四驅標配,我們去除別克朗科拉頂配四驅旗艦型,採用兩驅都市精英型(次頂配),與XR-V 1.8L CVT豪華版(頂配)對比,兩者指導價格和配置更加接近。
東風本田-XR-V
1.8L VTi CVT豪華版
指導價格:16.28萬
別克昂科拉
18T 自動兩驅都市精英型
指導價格:16.99萬
外觀:敦實沉穩VS時尚運動
昂科拉的整體外觀變化並不大,主要的變化在於前臉,車標加上飛翼式鍍鉻綬帶的裝飾安置於直瀑式中網上,鍍鉻綬帶還與前大燈模塊內的日間行車燈融為一體,這讓昂科拉的前臉顯得更加具有辨識度;
整車還是維持了昂科拉一貫的敦實形象,這麼一款尺寸不大的小型SUV看上去會讓人有一種沉穩紮實的感覺,腰線和車尾線條飽滿,配合上原廠提供的較為具有活力的配色,昂科拉的造型也透露出一種時尚的動感。
XR-V使用了當下本田家族式的上下雙條幅鍍鉻裝飾作為前臉主體設計元素,配合上銳利的大燈和大面積的黑色塑料裝飾,本田XR-V前臉顯得運動感與攻擊性都更為明顯。
相較於昂科拉的“敦厚”,XR-V的整車線條則顯得有些“扁”,這種效果就會使得車身側面的視覺效果顯得更加修長,車尾設計線條層次感豐富,橫向線條多次運用但不會顯得雜亂和複雜,反而更提升了XR-V年輕的效果。
內飾:各家所長得以充分體現
別克家族近年來的內飾營造手段是體現在全系車型上的,昂科拉亦是如此,乍一眼看上去,別克昂科拉的內飾設計很容易給人以好感,而且在用料的選材和裝配上,別克昂克拉的內飾顯得比較高檔。
而XR-V的內飾設計感或許稍微欠缺,而且由於成本所限,拼接裝飾板材質較硬,在對比體驗上說或許溝通感有所減分。但本田擅長的空間攫取能力則得以比較充分的體現,儘管XR-V的車身三圍尺寸並不算大,但在乘坐空間的表現上,好感度要比同級別的昂科拉更勝一籌。
動力:非常經典的對決
別克昂科拉與本田XR-V的對比也可以說是很多人十分在意的“渦輪增壓與自然吸氣的對比”,別克昂科拉採用1.4T渦輪增壓發動機配合6速手自一體的動力總成。最大馬力143匹,峰值扭矩205牛米。
昂科拉的峰值扭矩平台在發動機達到1800轉的時候得以爆發,對於這個級別的小排量渦輪增壓車型來說中規中矩,兩驅版本的車重1.4噸,動力輸出也不會因此而感到拖沓,換擋效率較高的手自一體變速箱也賦予了昂科拉一定的駕駛樂趣。
本田XR-V所搭載的是一台1.8L自然吸氣發動機,最大馬力136匹,峰值扭矩169牛米,與之配合的是一台CVT無極變速箱,整體駕駛感受保留了一台CVT該有的平順特性,駕駛樂趣的話,小編覺得談不上,但踏實平穩才是一款CVT車型該有的基調。
而且由於XR-V整備質量不大,頂配CVT豪華版車重1.3噸,加之本田CVT變速箱一貫良好的加速性能,XR-V在城市裡通勤的表現也是非常靈活,駕駛起來沒有太大難度。
哪個更值得買?
如果看重的是整車所給予人的質感和高級感方面,別克昂科拉是比較好的選擇,畢竟在車內裝飾用料和整車NVH的控制上,別克昂科拉可以說做到了領先於同級別其他車型的水準,昂科拉所給予人的是更高檔的使用感受。
而如果更看重的是車內更實用的空間表現和駕駛起來的平順性的話,本田XR-V或許更值得考慮,畢竟這兩塊是本田這個品牌所擅長的領域,而且在XR-V上也得以較好的體現,無論是乘坐空間還是載物空間,本田依舊展現了較強的空間利用實力,在動力平順性上,本田的CVT變速箱和自然吸氣發動機配合出來的線性感受也不會使人失望。
全文總結:儘管2017款的昂科拉在12月6日才剛剛上市,但是外觀配置並沒有過多的變化,而且在售價上與2016款幾乎齊平;加之在終端優惠上,別克全系車型當下的終端優惠普遍不小,而反觀日系的XR-V,降價的幅度則比較有限,所以如果從終端優惠上看,或許選擇折扣更大的別克昂科拉也是個不錯的想法。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※台北網頁設計公司全省服務真心推薦
※想知道最厲害的網頁設計公司"嚨底家"!
※推薦評價好的iphone維修中心
※網頁設計最專業,超強功能平台可客製化
※別再煩惱如何寫文案,掌握八大原則!
【摘要】我們從人臉識別技術的技術細節講起,帶你初步了解人臉識別技術的發展過程。通過平台實例的操作,帶你看看如何利用公有雲的計算資源,快速訓練一個可用的人臉識別模型。
大家應該都看過布拉德.伯德執導、湯姆.克魯斯主演的《碟中諜4吧》?茫茫人海的火車站,只要一眨眼的功夫已經被計算機識別出來,隨即被特工盯梢;迎面相逢的美女是致命殺手,手機發出嘀嘀的報警聲,上面已經显示美女的姓名和信息。這就是本文想要介紹的人臉識別算法,以及如果使用公有雲AI平台訓練模型。
作為目前人工智能領域中成熟較早、落地較廣的技術之一,人臉識別的目的是要判斷圖片和視頻中人臉的身份。從平常手機的刷臉解鎖、刷臉支付,再到安防領域內的人臉識別布控,等等,人臉識別技術都有着廣泛的應用。人臉是每個人與生俱來的特徵,該特徵具有唯一性並且不易被複制,從而為身份鑒別提供了必要的前提。
人臉識別的研究始於20世紀60年代,隨着計算機技術和光學成像技術的發展不斷提高,以及近幾年神經網絡技術的再次興起,尤其是卷積神經網絡在圖像識別和檢測中取得的巨大成功,使得人臉識別系統的效果得到了極大的提升。本文,我們從人臉識別技術的技術細節講起,帶你初步了解人臉識別技術的發展過程,文章的後半篇,我們將會使用ModelArts平台的自定義鏡像,帶你看看如何利用公有雲的計算資源,快速訓練一個可用的人臉識別模型。
不管是基於傳統圖像處理和機器學習技術,還是利用深度學習技術,其中的流程都是一樣的。如圖1所示,人臉識別系統都包括人臉檢測、對齊、編碼以及匹配四個基本環節組成。所以該部分首先通過對基於傳統圖像處理和機器學習算法的人臉識別系統進行概述,就可以看出整個深度學習算法在人臉識別領域內發展的脈絡。
人臉檢測流程
前面已經說過,人臉識別的目的就是要判斷圖像中的人臉身份是什麼,所以就首先需要先把圖像中的人臉檢測出來,其實這一步歸根結底就是一個目標檢測的問題。傳統的圖像目標檢測算法主要有三部分組成,建議框生成、特徵工程以及分類,包括著名的RCNN系列算法的優化思路也是基於這三部分進行的。
首先是建議框生成,該步驟最簡單的想法就是在圖片中crop出來一堆待檢測框,然後檢測該框內是否存在目標,如果存在,則該框在原圖中的位置即為目標檢測出的位置,因此在該步驟中對目標的覆蓋率越大,則建議框生成策略越好。常見的建議框生成策略有sliding window、Selective Search、Randomized Prim等等,生成大量的候選框,如下圖所示。
得到大量的候選框后,傳統的人臉檢測算法接下來最主要的部分就是特徵工程。特徵工程其實就是利用算法工程師的專家經驗對不同場景的人臉提取各種特徵,例如邊緣特徵、形狀形態學特徵、紋理特徵等等,具體的算法是技術有LBP、Gabor、Haar、SIFT等等特徵提取算法,將一張以二維矩陣表示的人臉圖片轉換成各種特徵向量的表示。
得到特徵向量之後,就可以通過傳統的機器學習分類器對特徵進行分類,得到是否是人臉的判斷,例如通過adaboost、cascade、SVM、隨機森林等等。通過傳統分類器分類之後就可以得到人臉的區域、特徵向量以及分類置信度等等。通過這些信息,我們就可以完成人臉對齊、特徵表示以及人臉匹配識別的工作。
以傳統方法中,經典的HAAR+AdaBoost的方法為例,在特徵提取階段,首先會利用haar特徵在圖片中提取出很多簡單的特徵。Haar特徵如下圖所示。為了滿足不同大小人臉的檢測,通常會利用高斯金字塔對不同分辨率的圖像進行Haar特徵的提取。
Haar特徵的計算方法是將白色區域內的像素和減去黑色區域,因此在人臉和非人臉的區域內,得到的值是不一樣的。一般在具體實現過程中,可以通過積分圖的方法快速實現。一般在歸一化到20*20的訓練圖片中,可供使用的Haar特徵數在一萬個左右,因此在這種特徵規模的情況下,可以利用機器學習的算法進行分類和識別。
得到Haar特徵后,可以利用Adaboost進行分類,Adaboost算法是一種將多個比較弱的分類方法合在一起,組合出新的強分類方法。根據該級聯分類器,和訓練好的各個特徵選擇閾值,就可以完成對人臉的檢測。
從上述方法可以看出,傳統的機器學習算法是基於特徵的算法,因此需要大量的算法工程師的專家經驗進行特徵工程和調參等工作,算法效果也不是很好。而且人工設計在無約束環境中對不同變化情況都魯棒很困難的。過去的圖像算法是工程師更多的是通過傳統的圖像處理方法,根據現實場景和專家經驗提取大量的特徵,然後對提取的特徵再進行統計學習的處理,這樣整體算法的性能就非常依賴於現實場景和專家經驗,對於人臉這種類別巨大,每類樣本不均衡情況嚴重的無約束場景效果並不是很好。因此,近幾年隨着深度學習在圖像處理中取得的巨大成功,人臉識別技術也都以深度學習為主,並且已經達到了非常好的效果。
在深度學習的人臉識別系統中,該問題被分成了一個目標檢測問題和一個分類問題,而目標檢測問題在深度學習中本質還是一個分類問題和回歸問題,因此隨着卷積神經網絡在圖片分類上的成功應用,人臉識別系統的效果得到了快速且巨大的提升,並以此誕生了大量的視覺算法公司,並將人臉識別應用在了社會生活的各個方面。
其實利用神經網絡來做人臉識別並不是什麼新思想,1997年就有研究者為人臉檢測、眼部定位和人臉識別提出了一種名為基於概率決策的神經網絡的方法。這種人臉識別 PDBNN 被分成了每一個訓練主體一個全連接子網絡,以降低隱藏單元的數量和避免過擬合。研究者使用密度和邊特徵分別訓練了兩個 PBDNN,然後將它們的輸出組合起來得到最終分類決定。但是受限於當時算力和數據的嚴重不足,算法相對簡單,因此該算法並沒有得到很好的效果。隨着僅今年反向傳播理論和算力框架等的日趨成熟,人臉識別算法的效果才開始得到巨大的提升。
在深度學習中,一個完整的人臉識別系統也包括圖1所示的四個步驟,其中第一步驟叫做人臉檢測算法,本質也是一個目標檢測算法。第二個步驟叫做人臉對齊,目前又基於關鍵點的幾何對齊和基於深度學習的人臉對齊。第三個步驟特徵表示,在深度學習中是通過分類網絡的思想,提取分類網絡中的一些feature層作為人臉的特徵表示,然後用相同的方式對標準人臉像進行處理,最後通過比對查詢的方式完成整體的人臉識別系統。下面主要對人臉檢測和人臉識別算法的發展進行簡單綜述。
深度學習在圖像分類中的巨大成功后很快被用於人臉檢測的問題,起初解決該問題的思路大多是基於CNN網絡的尺度不變性,對圖片進行不同尺度的縮放,然後進行推理並直接對類別和位置信息進行預測。另外,由於對feature map中的每一個點直接進行位置回歸,得到的人臉框精度比較低,因此有人提出了基於多階段分類器由粗到細的檢測策略檢測人臉,例如主要方法有Cascade CNN、 DenseBox和MTCNN等等。
MTCNN是一個多任務的方法,第一次將人臉區域檢測和人臉關鍵點檢測放在了一起,與Cascade CNN一樣也是基於cascade的框架,但是整體思路更加的巧妙合理,MTCNN總體來說分為三個部分:PNet、RNet和ONet,網絡結構如下圖所示。
首先PNet網絡對輸入圖片resize到不同尺寸,作為輸入,直接經過兩層卷積后,回歸人臉分類和人臉檢測框,這部分稱之為粗檢測。將粗檢測得到的人臉從原圖中crop出來后,在輸入的R-Net,再進行一次人臉檢測。最後將得到的人臉最終輸入O-Net,得到的O-Net輸出結果為最終的人臉檢測結果。MTCNN整體流程相對比較簡單,能夠快速的進行部署和實現,但是MTCNN的缺點也很多。包括多階段任務訓練費時,大量中間結果的保存需要佔用大量的存儲空間。另外,由於改網絡直接對feature點進行bounding box的回歸,對於小目標人臉檢測的效果也不是很好。還有,該網絡在推理的過程中為了滿足不同大小人臉檢測需要,要將人臉圖片resize到不同尺寸內,嚴重影響了推理的速度。
隨着目標檢測領域的發展,越來越多的實驗證據證明目標檢測中更多的瓶頸在於底層網絡語義低但定位精度相對較高和高層網絡語義高但定位精度低的矛盾,目標檢測網絡也開始流行anchor-based的策略和跨層融合的策略,例如著名的Faster-rcnn、SSD和yolo系列等。因此,人臉檢測算法也越來越多的利用anchor和多路輸出來滿足不同大小人臉檢出的效果,其中最著名的算法就是SSH網絡結構。
從上圖中可以看出,SSH網絡已經有對不同網絡層輸出進行處理的方法,只需要一遍推理就能完成不同大小人臉的檢測過程,因此稱之為Single Stage。SSH的網絡也比較簡單,就是對VGG不同卷積層驚醒了分支計算並輸出。另外還對高層feature進行了上採樣,與底層feature做Eltwise Sum來完成底層與高層的特徵融合。另外SSH網絡還設計了detection module和context module,其中context module作為detection module的一部分,採用了inception的結構,獲取更多上下文信息以及更大的感受野。
SSH中的detection module模塊SSH中detection module里的context module模塊
SSH利用1×1卷積對輸出最終的回歸和分類的分支結果,並沒有利用全連接層,因此可以保證不同尺寸圖片的輸入都能得到輸出的結果,也是響應了當時全卷積設計方式的潮流。遺憾的是該網絡並沒有輸出landmark點,另外其實上下文結構也沒有用到比較流行的特徵金字塔結構,VGG16的backbone也相對較淺,隨着人臉優化技術的不斷進行,各種各樣的trick也都日趨成熟。因此,最後向大家介紹一下目前人臉檢測算法中應用比較廣的Retinaface網絡。
Retinaface由google提出,本質是基於RetinaNet的網絡結構,採用特徵金字塔技術,實現了多尺度信息的融合,對檢測小物體有重要的作用。網絡結構如下所示。
從上圖可以看出,Retinaface的backbone網絡為常見的卷積神經網絡,然後加入特徵金子塔結構和Context Module模塊,進一步融合上下文的信息,並完成包括分類、檢測、landmark點回歸以及圖像自增強的多種任務。
因為人臉檢測的本質是目標檢測任務,目標檢測未來的方向也適用於人臉的優化方向。目前在目標檢測中小目標、遮擋目標的檢測依舊很困難,另外大部份檢測網絡更多的開始部署在端側,因此基於端側的網絡模型壓縮和重構加速等等更加考驗算法工程師對與深度學習檢測算法的理解和應用。
人臉識別問題本質是一個分類問題,即每一個人作為一類進行分類檢測,但實際應用過程中會出現很多問題。第一,人臉類別很多,如果要識別一個城鎮的所有人,那麼分類類別就將近十萬以上的類別,另外每一個人之間可獲得的標註樣本很少,會出現很多長尾數據。根據上述問題,要對傳統的CNN分類網絡進行修改。
我們知道深度卷積網絡雖然作為一種黑盒模型,但是能夠通過數據訓練的方式去表徵圖片或者物體的特徵。因此人臉識別算法可以通過卷積網絡提取出大量的人臉特徵向量,然後根據相似度判斷與底庫比較完成人臉的識別過程,因此算法網絡能不能對不同的人臉生成不同的特徵,對同一人臉生成相似的特徵,將是這類embedding任務的重點,也就是怎麼樣能夠最大化類間距離以及最小化類內距離。
在人臉識別中,主幹網絡可以利用各種卷積神經網絡完成特徵提取的工作,例如resnet,inception等等經典的卷積神經網絡作為backbone,關鍵在於最後一層loss function的設計和實現。現在從兩個思路分析一下基於深度學習的人臉識別算法中各種損失函數。
思路1:metric learning,包括contrastive loss, triplet loss以及sampling method
思路2:margin based classification,包括softmax with center loss, sphereface, normface, AM-sofrmax(cosface) 和arcface。
1. Metric Larning
(1)Contrastive loss
深度學習中最先應用metric learning思想之一的便是DeepID2了。其中DeepID2最主要的改進是同一個網絡同時訓練verification和classification(有兩個監督信號)。其中在verification loss的特徵層中引入了contrastive loss。
Contrastive loss不僅考慮了相同類別的距離最小化,也同時考慮了不同類別的距離最大化,通過充分運用訓練樣本的label信息提升人臉識別的準確性。因此,該loss函數本質上使得同一個人的照片在特徵空間距離足夠近,不同人在特徵空間里相距足夠遠直到超過某個閾值。(聽起來和triplet loss有點像)。
Contrastive loss引入了兩個信號,並通過兩個信號對網絡進行訓練。其中識別信號的表達式如下:
驗證信號的表達式如下:
基於這樣的信號,DeepID2在訓練的時候就不是以一張圖片為單位了,而是以Image Pair為單位,每次輸入兩張圖片,為同一人則為1,如果不是同一人則為-1.
(2)Triplet loss from FaceNet
這篇15年來自Google的FaceNet同樣是人臉識別領域分水嶺性質的工作。它提出了一個絕大部分人臉問題的統一解決框架,即:識別、驗證、搜索等問題都可以放到特徵空間里做,需要專註解決的僅僅是如何將人臉更好的映射到特徵空間。
Google在DeepID2的基礎上,拋棄了分類層即Classification Loss,將Contrastive Loss改進為Triplet loss,只為了一個目的:學習到更好的feature。
直接貼出Triplet loss的損失函數,其輸入的不再是Image Pair,而是三張圖片(Triplet),分別為Anchor Face, Negative Face和Positive Face。Anchor與Positive Face為同一人,與Negative Face為不同的人。那麼Triplet loss的損失函數即可表示為:
該式子的直觀解釋為:在特徵空間里Anchor與Positive的距離要小於Anchor與Negative的距離並超過一個Margin Alpha。他與Contrastive loss的直觀區別由下圖所示。
(3)Metric learning的問題
上述的兩個loss function效果很不錯,而且也符合人的客觀認知,在實際項目中也有大量的應用,但該方法仍有一些不足之處。
2. 對於Metric Learning不足進行修正的各種trick
(1)Finetune
參考論文:Deep Face Recognition
在論文《Deep Face Recognition》中,為了加快triplet loss的訓練,坐着先用softmax訓練人臉識別模型,然後移除頂層的classification layer,然後用triplet loss對模型進行特徵層finetune,在加速訓練的同時也取得了很不錯的效果。該方法也是現在訓練triplet loss時最常用的方法。
(2)對Triplet loss的修改
參考論文:In Defense of the Triplet Loss for Person Re-Identification
該作者說出了Triplet loss的缺點。對於Triplet loss訓練所需要的一個三元組,anchor(a)、positive(p)、negative(n)來說,需要從訓練集中隨機挑選。由於loss function的驅動,很有可能挑選出來的是很簡單的樣本組合,即很像的正樣本以及很不像的負樣本,而讓網絡一直在簡單樣本上進行學習,會限制網絡的范化能力。因此坐着修改了triplet loss並添加了新的trick,大量實驗證明,這種改進版的方法效果非常好。
在Google提供的facenet triplet loss訓練時,一旦選定B triplets集合,數據就會按照順序排好的3個一組,那麼總共的組合就有3B種,但是這些3B個圖像實際上有多達種有效的triplets組合,僅僅使用3B種就很浪費。
在該片論文中,作者提出了一個TriHard loss,其核心思想是在triplet loss的基礎上加入對hard example的處理:對於每一個訓練的batch, 隨機挑選P個ID的行人,每個行人隨機挑選K張不同的圖片,即一個batch含有P×K張圖片。之後對於batch中的每一張圖片a,我們可以挑選一個最難的正樣本和一個最難的負樣本和a組成一個三元組。首先我們定義和a為相同ID的圖片集為A,剩下不同ID的圖片圖片集為B,則TriHard損失表示為:
其中是人為設定的閾值參數。TriHard loss會計算a和batch中的每一張圖片在特徵空間的歐氏距離,然後選出與a距離最遠(最不像)的正樣本p和距離最近(最像)的負樣本n來計算三元組損失。其中d表示歐式距離。損失函數的另一種寫法如下:
另外,作者在輪中也提出了幾個實驗得到的觀點:
該方法考慮了hard example後效果比傳統的triplet loss好。
(3)對loss以及sample方法的修改
參考論文:Deep Metric Learning via Lifted Structured Feature Embedding
該論文首先提出了現有的三元組方法無法充分利用minibatch SGD training的training batches的優勢,創造性的將the vector of pairwise distances轉換成the matrix of pairwise distance,然後設計了一個新的結構化損失函數,取得了非常好的效果。如下圖所示,是contrastice embedding,triplet embedding以及lifted structured embedding三種方式的採樣示意圖。
直觀上看,lifted structured embedding涉及的分類模式更多,作者為了避免大量數據造成的訓練困難,作者在此基礎上給出了一個結構化的損失函數。如下圖所示。
其中P是正樣本集合,N是負樣本集合。可以看到對比上述的損失函數,該損失函數開始考慮一個樣本集合的問題。但是,並不是所有樣本對之間的negative edges都攜帶了有用的信息,也就是說隨機採樣的樣本對之間的negative edges攜帶了非常有限的信息,因此我們需要設計一種非隨機的採樣方法。
通過上述的結構化損失函數我們可以看到,在最終計算損失函數時,考慮了最像和最不像的hard pairs(也就是損失函數中max的用處),也就相當於在訓練過程中添加了difficult neighbors的信息了訓練mini-batch,通過這種方式訓練數據能夠大概率的搜尋到hard negatives和hard positives的樣本,而隨着訓練的不斷進行,對hard樣本的訓練也將實現最大化類間距離和最小化類內距離的目的。
如上圖所示,該文章在進行metric learning的時候並沒有隨機的選擇sample pairs,而是綜合了多類樣本之間較難區分者進行訓練。此外,文中還提到了以為的尋求max的過程或者尋求single hardest negative的過程會導致網絡收斂到一個bad local optimum,我猜想可能是因為max的截斷效應,使得梯度比較陡峭或者梯度間斷點過多。作者進一步改進了loss function,採用了smooth upper bound,即下式所示。
(4)對sample方式和對triplet loss的進一步修改
參考論文:Sampling Matters in Deep Embedding Learning
文章指出hard negative樣本由於anchor的距離較小,這是如果有噪聲,那麼這種採樣方式就很容易受到噪聲的影響,從而造成訓練時的模型坍塌。FaceNet曾經提出一種semi-hard negative mining的方法,它提出的方法是讓採樣的樣本不是太hard。但是根據作者的分析認為,sample應該在樣本中進行均勻的採樣,因此最佳的採樣狀態應該是在分散均勻的負樣本中,既有hard,又有semi-hard,又有easy的樣本,因此作者提出了一種新的採樣方法Distance weighted sampling。
在現實狀態下,我們隊所有的樣本進行兩兩採樣,計算其距離,最終得到點對距離的分佈有着如下的關係:
那麼根據給定的距離,通過上述函數的反函數就可以得到其採樣概率,根據該概率決定每個距離需要採樣的比例。給定一個anchor,採樣負例的概率為下式:
由於訓練樣本與訓練梯度強相關,因此作者也繪製出了採樣距離、採樣方法與數據梯度方差的關係,如下圖所示。從圖中可以看出,hard negative mining方法採樣的樣本都處於高方差的區域,如果數據集中有噪聲的話,採樣很容易受到噪聲的影響,從而導致模型坍塌。隨機採樣的樣本容易集中在低方差的區域,從而使得loss很小,但此時模型實際上並沒有訓練好。Semi-hard negative mining採樣的範圍很小,這很可能導致模型在很早的時候就收斂,loss下降很慢,但實際上此時模型也還沒訓練好;而本文提出的方法,能夠實現在整個數據集上均勻採樣。
作者在觀察constractive loss和triplet loss的時候發現一個問題,就是負樣本在非常hard的時候loss函數非常的平滑,那麼也就意味着梯度會很小,梯度小對於訓練來說就意味着非常hard的樣本不能充分訓練,網絡得不到hard樣本的有效信息,因此hard樣本的效果就會變差。所以如果在hard樣本周圍loss不是那麼平滑,也就是深度學習中經常用的導數為1(像relu一樣),那麼hard模式會不會就解決了梯度消失的問題。另外loss function還要實現triplet loss對正負樣本的兼顧,以及具備margin設計的功能,也就是自適應不同的數據分佈。損失函數如下:
我們稱anchor樣本與正例樣本之間的距離為正例對距離;稱anchor樣本與負例樣本之間的距離為負例對距離。公式中的參數beta定義了正例對距離與負例對距離之間的界限,如果正例對距離Dij大於beta,則損失加大;或者負例對距離Dij小於beta,損失加大。A控制樣本的分離間隔;當樣本為正例對時,yij為1,樣本為負例對時,yij為-1。下圖為損失函數曲線。
從上圖可以看出為什麼在非常hard的時候會出現梯度消失的情況,因為離0點近的時候藍色的線越來越平滑,梯度也就越來越小了。另外作者對的設置也進行了調優,加入了樣本偏置、類別偏置以及超參,對損失函數進一步優化,能夠根據訓練過程自動修改的值。
3. Margin Based Classification
Margin based classification不像在feature層直接計算損失的metric learning那樣對feature加直觀的強限制,是依然把人臉識別當 classification 任務進行訓練,通過對 softmax 公式的改造,間接實現了對 feature 層施加 margin 的限制,使網絡最後得到的 feature 更 discriminative。
(1)Center loss
參考論文:A Discriminative Feature Learning Approach for Deep Face Recognition
ECCV 2016的這篇文章主要是提出了一個新的Loss:Center Loss,用以輔助Softmax Loss進行人臉的訓練,為了讓同一個類別壓縮在一起,最終獲取更加discriminative的features。center loss意思即為:為每一個類別提供一個類別中心,最小化min-batch中每個樣本與對應類別中心的距離,這樣就可以達到縮小類內距離的目的。下圖為最小化樣本和類別中心距離的損失函數。
為每個batch中每個樣本對應的類別中心,和特徵的維度一樣,用歐式距離作為高維流形體距離表達。因此,在softmax的基礎上,center loss的損失函數為:
個人理解Center loss就如同在損失函數中加入了聚類的功能,隨着訓練的進行,樣本自覺地聚類在每一個batch的中心,進一步實現類間差異最大化。但是我覺得,對於高維特徵,歐氏距離並不能反映聚類的距離,因此這樣簡單的聚類並不能在高維上取得更好的效果。
(2)L-Softmax
原始的Softmax的目的是使得,將向量相乘的方式變換為向量的模與角度的關係,即,在這個基礎上,L-Softmax希望可以通過增加一個正整數變量m,可以看到:
使得產生的決策邊界可以更加嚴格地約束上述不等式,讓類內的間距更加的緊湊,讓類間的間距更加有區分性。所以基於上式和softmax的公式,可以得到L-softmax的公式為:
由於cos是減函數,所以乘以m會使得內積變小,最終隨着訓練,類本身之間的距離會增大。通過控制m的大小,可以看到類內和類間距離的變化,二維圖显示如下:
作者為了保障在反向傳播和推理過程中能夠滿足類別向量之間的角度都能夠滿足margin的過程,並保證單調遞減,因此構建了一種新的函數形式:
有人反饋L-Softmax調參難度較大,對m的調參需要反覆進行,才能達到更好的效果。
(3)Normface
參考論文:NormFace: L2 Hypersphere Embedding for Face Verification
這篇論文是一篇很有意思的文章,文章對於權重與特徵歸一化做了很多有意思的探討。文章提出,sphereface雖然好,但是它不優美。在測試階段,sphereface通過特徵間的餘弦值來衡量相似性,即以角度為相似性度量。但在訓練過程中也有一個問題,權重沒有歸一化,loss function在訓練過程中減小的同時,會使得權重的模越來越大,所以sphereface損失函數的優化方向並不是很嚴謹,其實優化的方向還有一部分去增大特徵的長度了。有博主做實驗發現,隨着m的增大,坐標的尺度也在不斷增大,如下圖所示。
因此作者在優化的過程中,對特徵做了歸一化處理。相應的損失函數也如下所示:
其中W和f都為歸一化的特徵,兩個點積就是角度餘弦值。參數s的引入是因為數學上的性質,保證了梯度大小的合理性,原文中有比較直觀的解釋,可以閱讀原論文,並不是重點。s既可以變成可學習的參數,也可以變成超參,論文作者給了很多推薦值,可以在論文中找到。其實,FaceNet中歸一化的歐氏距離,和餘弦距離是統一的。
4. AM-softmax/CosFace
參考論文:Additive Margin Softmax for Face Verification
CosFace: Large Margin Cosine Loss for Deep Face Recognition
看上面的論文,會發現少了一個東西,那就是margin,或者說是margin的意味少了一些,所以AM-softmax在歸一化的基礎上有引入了margin。損失函數如下:
直觀上來看,-m比更小,所以損失函數值比Normface里的更大,因此有了margin的感覺。m是一個超參數,控制懲罰,當m越大,懲罰越強。該方法好的一點是容易復現,而且沒有很多調參的tricks,效果也很好。
(1)ArcFace
與 AM-softmax 相比,區別在於 Arcface 引入 margin 的方式不同,損失函數:
乍一看是不是和 AM-softmax一樣?注意 m 是在餘弦裏面。文章指出基於上式優化得到的特徵間的 boundary 更為優越,具有更強的幾何解釋。
然而這樣引入 margin 是否會有問題?仔細想 cos(θ+m) 是否一定比 cos(θ) 小?
最後我們用文章中的圖來解釋這個問題,並且也由此做一個本章 Margin-based Classification 部分的總結。
這幅圖出自於 Arcface,橫坐標為 θ 為特徵與類中心的角度,縱坐標為損失函數分子指數部分的值(不考慮 s),其值越小損失函數越大。
看了這麼多基於分類的人臉識別論文,相信你也有種感覺,大家似乎都在損失函數上做文章,或者更具體一點,大家都是在討論如何設計上圖的 Target logit-θ 曲線。
這個曲線意味着你要如何優化偏離目標的樣本,或者說,根據偏離目標的程度,要給予多大的懲罰。兩點總結:
1. 太強的約束不容易泛化。例如 Sphereface 的損失函數在 m=3 或 4 的時候能滿足類內最大距離小於類間最小距離的要求。此時損失函數值很大,即 target logits 很小。但並不意味着能泛化到訓練集以外的樣本。施加太強的約束反而會降低模型性能,且訓練不易收斂。
2. 選擇優化什麼樣的樣本很重要。Arcface 文章中指出,給予 θ∈[60° , 90°] 的樣本過多懲罰可能會導致訓練不收斂。優化 θ ∈ [30° , 60°] 的樣本可能會提高模型準確率,而過分優化 θ∈[0° , 30°] 的樣本則不會帶來明顯提升。至於更大角度的樣本,偏離目標太遠,強行優化很有可能會降低模型性能。
這也回答了上一節留下的疑問,上圖曲線 Arcface 後面是上升的,這無關緊要甚至還有好處。因為優化大角度的 hard sample 可能沒有好處。這和 FaceNet 中對於樣本選擇的 semi-hard 策略是一個道理。
1. A discriminative feature learning approach for deep face recognition [14]
提出了 center loss,加權整合進原始的 softmax loss。通過維護一個歐式空間類中心,縮小類內距離,增強特徵的 discriminative power。
2. Large-margin softmax loss for convolutional neural networks [10]
Sphereface 作者的前一篇文章,未歸一化權重,在 softmax loss 中引入了 margin。裏面也涉及到 Sphereface 的訓練細節。
人臉識別算法實現解釋
本文我們部署的人臉識別算法模型主要包括兩部分:
如下圖所示,整體算法實現的流程分為線下和線上兩個部分,在每次對不同的人進行識別之前首先利用訓練好的算法生成人臉標準底庫,將底庫數據保存在modelarts上。然後在每次推理的過程中,圖片輸入會經過人臉檢測模型和人臉識別模型得到人臉特徵,然後基於該特徵在底庫中搜索相似對最高的特徵,完成人臉識別的過程。
在實現過程中,我們採用了基於Retinaface+resnet50+arcface的算法完成人臉圖像的特徵提取,其中Retinaface作為檢測模型,resnet50+arcface作為特徵提取模型。
在鏡像中,運行訓練的腳本有兩個,分別對應人臉檢測的訓練和人臉識別的訓練。
run_face_detection_train.sh
該腳本的啟動命令為
sh run_face_detection_train.sh data_path model_output_path
其中model_output_path為模型輸出的路徑,data_path為人臉檢測訓練集的輸入路徑,輸入的圖片路徑結構如下:
detection_train_data/ train/ images/ label.txt val/ images/ label.txt test/ images/ label.txt
run_face_recognition_train.sh
該腳本的啟動命令為
sh run_face_recognition_train.sh data_path model_output_path
其中model_output_path為模型輸出的路徑,data_path為人臉檢測訓練集的輸入路徑,輸入的圖片路徑結構如下:
recognition_train_data/
cele.idx
cele.lst
cele.rec
property
run_generate_data_base.sh
該腳本的啟動命令為:
sh run_generate_data_base.sh data_path detect_model_path recognize_model_path db_output_path
其中data_path為底庫輸入路徑,detect_model_path為檢測模型輸入路徑,recognize_model_path為識別模型輸入路徑,db_output_path為底庫輸出路徑。
run_face_recognition.sh
該腳本的啟動命令為:
sh run_generate_data_base.sh data_path db_path detect_model_path recognize_model_path
其中data_path為測試圖片輸入路徑,db_path為底庫路徑,detect_model_path為檢測模型的輸入路徑,recognize_model_path為識別模型的輸入路徑
華為雲ModelArts有訓練作業的功能,可以用來作模型訓練以及對模型訓練的參數和版本進行管理。這個功能對於多版本迭代開發的開發者有一定的幫助。訓練作業中有預置的一些鏡像和算法,當前對於常用的框架均有預置鏡像(包括Caffe, MXNet, Pytorch, TensorFlow )和華為自己的昇騰芯片的引擎鏡像(Ascend-Powered-Engine)。
本文我們會基於ModelArts的自定義鏡像特性,上傳自己在本機調試完畢的完整鏡像,利用華為雲的GPU資源訓練模型。
我們是想在華為雲上的ModelArts基於網站上常見的明星的數據訓練完成一個人臉識別模型。在這個過程中,由於人臉識別網絡是工程師自己設計的網絡結構,所以需要通過自定義鏡像進行上傳。所以整個人臉訓練的過程分為以下九步:
構建本地Docker環境
Docker環境可以在本地計算機進行構建,也可以在華為雲上購買一台彈性雲服務器進行Docker環境構建。全過程參考Docker官方的文檔進行:
https://docs.docker.com/engine/install/binaries/#install-static-binaries
從華為雲下載基礎鏡像
官網說明網址:
https://support.huaweicloud.com/engineers-modelarts/modelarts_23_0085.html#modelarts_23_0085__section19397101102
我們訓練需要使用到的是MXNet的環境,首先需要從華為雲上下載相對應的自定義鏡像的基礎鏡像。官網給出的下載命令如下:
在訓練作業基礎鏡像的規範里,找到了這個命令的解釋。
https://support.huaweicloud.com/engineers-modelarts/modelarts_23_0217.html
根據我們的腳本要求,我使用的是cuda9的鏡像:
官方還給出了另一種方法,就是使用docker file的。基礎鏡像的dockerfile也是在訓練作業基礎鏡像的規範里找到的。可以參考一下的dockerfile:
https://github.com/huaweicloud/ModelArts-Lab/tree/master/docs/custom_image/custom_base
根據自己需求構建自定義鏡像環境
由於比較懶,所以還是沒有使用Dockerfile的方式自己構建鏡像。我採用的是另一種方式!
因為我們的需求就是cuda 9 還有一些相關的python依賴包,假設官方的鏡像提供的是cuda 9的,我們大可以在訓練腳本中跟着這個教程加一個requirement.txt。簡單高效快捷就能解決需求!!!下面是教程~~~
https://support.huaweicloud.com/modelarts_faq/modelarts_05_0063.html
上傳自定義鏡像到SWR
官網教程:
上傳鏡像的頁面寫着,文件解壓后不得超過2GB。但是官方提供的基礎鏡像就3.11GB,我們加上需要的預訓練的模型後鏡像是5+GB,所以不能使用頁面進行上傳的工作,必須使用客戶端。上傳鏡像首先要創建組織,
如果覺得產品文檔理解還是比較難,可以嘗試一下SWR頁面的pull/push鏡像體驗:
這裏後面引導了客戶如何將本地鏡像推上雲端,第一步是登陸倉庫:
第二步拉取鏡像,這個我們就用自己打的自定義鏡像代替,
第三步修改組織,使用根據產品文檔創建的組織名。在這一步需要將本地的一個鏡像重命名為雲上識別的鏡像命。具體看下圖解釋:
第四步推送鏡像,
當熟練掌握這四步技巧的時候,可以脫離這個教程,使用客戶端進行上傳。使用客戶端登陸然後上傳。客戶端登陸可以使用生成臨時docker loging指令。這個頁面在”我的鏡像“-> ”客戶端上傳“->”生成臨時docker login指令“中:
在本地docker環境中,使用這個生成的臨時docker login指令登陸后,使用下面的命令進行上傳鏡像:
華為雲ModelArts提供訓練作業給用戶進行模型訓練。在訓練作業中有預置鏡像和可以選擇自定義鏡像。預置的鏡像包含市面上大部分框架,沒有特殊要求的時候,使用這些框架的鏡像進行訓練也是很方便的。本次測試還是使用的自定義鏡像。
自定義鏡像中不僅需要在鏡像中進行配置自己的環境,假如改變了訓練作業啟動的方式,還需要修改訓練的啟動腳本。從華為雲ModelArts官網拉取下來的官方鏡像的/home/work/路徑下有一個啟動腳本”run_train.sh”,自定義的啟動腳本需要基於這個腳本進行修改。主要是要注意 “dls_get_app”,這個是從OBS下載相關的命令。其他的部分根據自己的訓練腳本進行修改。
如果需要上傳訓練結果或者模型到OBS,需要參考”dls_get_app”加”dls_upload_model”的命令。在我們這次訓練中,上傳的腳本如下:
訓練作業進行調試的時候,當前可以使用免費提供的一小時V100。ModelArts的訓練作業一個比較好的地方是方便了我們版本管理。版本中會記錄所有通過運行參數傳入到訓練腳本里的所有參數,還可以使用版本對比進行參數對比。還有個比較方便的地方是可以基於某一個版本進行修改,減少了重新輸入所有參數這一步驟,比較方便調試。
在訓練作業中訓練完成后,還可以在ModelArts中進行模型部署上線。
目前針對人臉識別算法的優化已經到達一個瓶頸期,但是在技術層面針對人臉面部結構的相似性、人臉的姿態、年齡變化、複雜環境的光照變化、人臉的飾物遮擋等還面臨這很多的問題,因此基於多種算法技術的融合解決人臉識別中的各種問題仍然在安防、互聯網中有着巨大的市場。另外,隨着人臉支付的逐漸完善,人臉識別系統也應用於銀行、公安系統、商場等等,因此人臉識別的安全問題和防攻擊問題也是一個亟待解決的問題,例如活體檢測、3D面部識別等等。
最後,人臉識別作為目前深度學習中應用比較成熟的項目,其發展還與深度學習本身技術發展息息相關,目前在很多優化上,深度學習最大的缺點是沒有相應的數學理論支撐,優化所提升的性能也很有限,因此對深度學習算法本身的研究也是未來的重點。
點擊關注,第一時間了解華為雲新鮮技術~
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※台北網頁設計公司全省服務真心推薦
※想知道最厲害的網頁設計公司"嚨底家"!
※推薦評價好的iphone維修中心
※網頁設計最專業,超強功能平台可客製化
※別再煩惱如何寫文案,掌握八大原則!
這個階段會給cute-dl添加循環層,使之能夠支持RNN–循環神經網絡. 具體目標包括:
RNN模型用來捕捉序列數據的特徵. 給定一個長度為T的輸入系列\(X=(x_1, x_2, .., X_T)\), RNN層輸出一個長度為T的序列\(H=(h_1, h_2, …, H_T)\), 對於任意時間步t, 可以表示為:
\[H_t = δ(X_tW_x + H_{t-1}W_h + b), \quad t = 2, 3, .., T \]
函數δ是sigmoid函數:
\[δ = \frac{1}{1 + e^{-x}} \]
\(H_t\)包含了前面第1到t-1步的所有信息。 和CNN層類似, CNN層在空間上共享參數, RNN層在時間步上共享參數\(W_x, W_h, b\).
RNN層中隱藏層的數量為T-2, 如果T較大(超過10), 反向傳播是很容易出現梯度爆炸. GRU和LSTM就是為了解決這個問題而誕生, 這兩種模型,可以讓RNN能夠支持長度超過1000的輸入序列。
GRU使用了不同功能的門控單元, 分別捕捉序列上不同時間跨度的的依賴關係。每個門控單元都會都有獨立的參數, 這些參數在時間步上共享。
GRU的門控單元有:
\(R_t = δ(X_tW^r_x + H_{t-1}W^r_h + b^r)\), 重置門用於捕捉短期依賴關係.
\(U_t = δ(X_tW^u_x + H_{t-1}W^u_h + b^u)\), 更新門用於捕捉長期依賴關係
\(\bar{H}_t = tanh(X_t\bar{W}_x + (R_t * H_{t-1})\bar{W}_h + \bar{b})\)
除此之外, 還有一個輸出單元:
\(H_t = U_t * H_{t-1} + (1-U_t)*\bar{H}_t\)
LSTM的設計思路和GRU類似, 同樣使用了多個門控單元:
\(I_t = δ(X_tW^i_x + H_{t-1}W^i_h + b^i)\), 輸入門,過濾記憶門的輸出.
\(F_t = δ(X_tW^f_x + H_{t-1}W^f_h + b^f)\), 遺忘門, 過濾前面時間步的記憶.
\(O_t = δ(X_tW^o_x + H_{t-1}W^o_h + b^o)\), 輸出門, 過濾當前時間步的記憶.
\(M_t = tanh(X_tW^m_x + H_{t-1}W^m_h + b^m)\), 記憶門.
它還有自己獨有的記憶單元和輸出單元:
\(\bar{M}_t = F_t * \bar{M}_{t-1} + I_t * M_t\)
\(H_t = O_t * tanh(\bar{M}_t)\)
設計要求:
文件: cutedl/rnn_layers.py, 類名: RNN
這個類是RNN層基類, 它主要功能是控制向前傳播和向後傳播的主流程.
初始化參數:
'''
out_units 輸出單元數
in_units 輸入單元數
stateful 保留當前批次的最後一個時間步的狀態作為下一個批次的輸入狀態, 默認False不保留
RNN 的輸入形狀是(m, t, in_units)
m: batch_size
t: 輸入系列的長度
in_units: 輸入單元數頁是輸入向量的維數
輸出形狀是(m, t, out_units)
'''
def __init__(self, out_units, in_units=None, stateful=False, activation='linear'):
向前傳播
def forward(self, in_batch, training):
m, T, n = in_batch.shape
out_units = self.__out_units
#所有時間步的輸出
hstatus = np.zeros((m, T, out_units))
#上一步的輸出
pre_hs = self.__pre_hs
if pre_hs is None:
pre_hs = np.zeros((m, out_units))
#隱藏層循環過程, 沿時間步執行
for t in range(T):
hstatus[:, t, :] = self.hiden_forward(in_batch[:,t,:], pre_hs, training)
pre_hs = hstatus[:, t, :]
self.__pre_hs = pre_hs
#pdb.set_trace()
if not self.stateful:
self.__pre_hs = None
return hstatus
反向傳播
def backward(self, gradient):
m, T, n = gradient.shape
in_units = self.__in_units
grad_x = np.zeros((m, T, in_units))
#pdb.set_trace()
#從最後一個梯度開始反向執行.
for t in range(T-1, -1, -1):
grad_x[:,t,:], grad_hs = self.hiden_backward(gradient[:,t,:])
#pdb.set_trace()
if t - 1 >= 0:
gradient[:,t-1,:] = gradient[:,t-1,:] + grad_hs
#pdb.set_trace()
return grad_x
\[sigmoid = \frac{1}{1+e^{-x}} \]
\[\frac{d}{dx}sigmoid = sigmoid(1-sigmoid) \]
\[tanh = \frac{e^x – e^{-x}}{e^x + e^{-x}} \]
\[\frac{d}{dx}tanh = 1 – tanh^2 \]
文件: cutedl/rnn_layers.py, 類名: GateUint
門控單元是RNN層基礎的參數單元. 和Dense層類似,它是Layer的子類,負責學習和使用參數。但在學習和使用參數的方式上有很大的不同:
下面我們會主要看一下GateUnit特別之處的代碼.
在__ init__方法中定義參數和棧:
#3個參數
self.__W = None #當前時間步in_batch權重參數
self.__Wh = None #上一步輸出的權重參數
self.__b = None #偏置量參數
#輸入棧
self.__hs = [] #上一步輸出
self.__in_batchs = [] #當前時間步的in_batch
正向傳播:
def forward(self, in_batch, hs, training):
W = self.__W.value
b = self.__b.value
Wh = self.__Wh.value
out = in_batch @ W + hs @ Wh + b
if training:
#向前傳播訓練時把上一個時間步的輸出和當前時間步的in_batch壓棧
self.__hs.append(hs)
self.__in_batchs.append(in_batch)
#確保反向傳播開始時參數的梯度為空
self.__W.gradient = None
self.__Wh.gradient = None
self.__b.gradient = None
return self.activation(out)
反向傳播:
def backward(self, gradient):
grad = self.activation.grad(gradient)
W = self.__W.value
Wh = self.__Wh.value
pre_hs = self.__hs.pop()
in_batch = self.__in_batchs.pop()
grad_in_batch = grad @ W.T
grad_W = in_batch.T @ grad
grad_hs = grad @ Wh.T
grad_Wh = pre_hs.T @ grad
grad_b = grad.sum(axis=0)
#反向傳播計算
if self.__W.gradient is None:
#當前批次第一次
self.__W.gradient = grad_W
else:
#累積當前批次的所有梯度
self.__W.gradient = self.__W.gradient + grad_W
if self.__Wh.gradient is None:
self.__Wh.gradient = grad_Wh
else:
self.__Wh.gradient = self.__Wh.gradient + grad_Wh
if self.__b.gradient is None:
self.__b.gradient = grad_b
else:
self.__b.gradient = self.__b.gradient + grad_b
return grad_in_batch, grad_hs
文件: cutedl/rnn_layers.py, 類名: GRU
隱藏單初始化:
def set_parent(self, parent):
super().set_parent(parent)
out_units = self.out_units
in_units = self.in_units
#pdb.set_trace()
#重置門
self.__g_reset = GateUnit(out_units, in_units)
#更新門
self.__g_update = GateUnit(out_units, in_units)
#候選輸出門
self.__g_cddout = GateUnit(out_units, in_units, activation='tanh')
self.__g_reset.set_parent(self)
self.__g_update.set_parent(self)
self.__g_cddout.set_parent(self)
#重置門乘法單元
self.__u_gr = MultiplyUnit()
#輸出單元
self.__u_out = GRUOutUnit()
向前傳播:
def hiden_forward(self, in_batch, pre_hs, training):
gr = self.__g_reset.forward(in_batch, pre_hs, training)
gu = self.__g_update.forward(in_batch, pre_hs, training)
ugr = self.__u_gr.forward(gr, pre_hs, training)
cddo = self.__g_cddout.forward(in_batch, ugr, training)
hs = self.__u_out.forward(gu, pre_hs, cddo, training)
return hs
反向傳播:
def hiden_backward(self, gradient):
grad_gu, grad_pre_hs, grad_cddo = self.__u_out.backward(gradient)
#pdb.set_trace()
grad_in_batch, grad_ugr = self.__g_cddout.backward(grad_cddo)
#計算梯度的過程中需要累積上一層輸出的梯度
grad_gr, g_pre_hs = self.__u_gr.backward(grad_ugr)
grad_pre_hs = grad_pre_hs + g_pre_hs
g_in_batch, g_pre_hs = self.__g_update.backward(grad_gu)
grad_in_batch = grad_in_batch + g_in_batch
grad_pre_hs = grad_pre_hs + g_pre_hs
g_in_batch, g_pre_hs = self.__g_reset.backward(grad_gr)
grad_in_batch = grad_in_batch + g_in_batch
grad_pre_hs = grad_pre_hs + g_pre_hs
#pdb.set_trace()
return grad_in_batch, grad_pre_hs
文件: cutedl/rnn_layers.py, 類名: LSTM
隱藏單元初始化:
def set_parent(self, layer):
super().set_parent(layer)
in_units = self.in_units
out_units = self.out_units
#輸入門
self.__g_in = GateUnit(out_units, in_units)
#遺忘門
self.__g_forget = GateUnit(out_units, in_units)
#輸出門
self.__g_out = GateUnit(out_units, in_units)
#記憶門
self.__g_memory = GateUnit(out_units, in_units, activation='tanh')
self.__g_in.set_parent(self)
self.__g_forget.set_parent(self)
self.__g_out.set_parent(self)
self.__g_memory.set_parent(self)
#記憶單元
self.__memory_unit =LSTMMemoryUnit()
#輸出單元
self.__out_unit = LSTMOutUnit()
向前傳播:
def hiden_forward(self, in_batch, hs, training):
g_in = self.__g_in.forward(in_batch, hs, training)
#pdb.set_trace()
g_forget = self.__g_forget.forward(in_batch, hs, training)
g_out = self.__g_out.forward(in_batch, hs, training)
g_memory = self.__g_memory.forward(in_batch, hs, training)
memory = self.__memory_unit.forward(g_forget, g_in, g_memory, training)
cur_hs = self.__out_unit.forward(g_out, memory, training)
return cur_hs
反向傳播:
def hiden_backward(self, gradient):
#pdb.set_trace()
grad_out, grad_memory = self.__out_unit.backward(gradient)
grad_forget, grad_in, grad_gm = self.__memory_unit.backward(grad_memory)
grad_in_batch, grad_hs = self.__g_memory.backward(grad_gm)
tmp1, tmp2 = self.__g_out.backward(grad_out)
grad_in_batch += tmp1
grad_hs += tmp2
tmp1, tmp2 = self.__g_forget.backward(grad_forget)
grad_in_batch += tmp1
grad_hs += tmp2
tmp1, tmp2 = self.__g_in.backward(grad_in)
grad_in_batch += tmp1
grad_hs += tmp2
return grad_in_batch, grad_hs
接下來, 驗證示例將會構建一個簡單的RNN模型, 使用該模型擬合一個正餘弦疊加函數:
#採樣函數
def sample_function(x):
y = 3*np.sin(2 * x * np.pi) + np.cos(x * np.pi) + np.random.uniform(-0.05,0.05,len(x))
return y
訓練數據集和測試數據集在這個函數的不同定義域區間內樣. 訓練數據集的採樣區間為[1, 200.01), 測試數據集的採樣區間為[200.02, 240.002). 模型任務是預測這個函數值的序列.
示例代碼在examples/rnn/fit_function.py文件中.
def fit_gru():
model = Model([
rnn.GRU(32, 1),
nn.Filter(),
nn.Dense(32),
nn.Dense(1, activation='linear')
])
model.assemble()
fit('gru', model)
訓練報告:
def fit_lstm():
model = Model([
rnn.LSTM(32, 1),
nn.Filter(),
nn.Dense(2),
nn.Dense(1, activation='linear')
])
model.assemble()
fit('lstm', model)
訓練報告:
這個階段,框架新增了RNN的兩個最常見的實現:GRU和LSTM, 相應地增加了它需要的激活函數. cute-dl已經具備了構建最基礎RNN模型的能力。通過驗證發現, GRU模型和LSTM模型在簡單任務上都表現出了很好的性能。會添加嵌入層,使框架能夠構建文本分類任務的模型,然後在imdb-review(電影評價)數據集上進行驗證.
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線
※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整
※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!
※教你寫出一流的銷售文案?
※超省錢租車方案
※回頭車貨運收費標準