最近做了很多小型的活動,往往是用戶會產(chǎn)生一些數(shù)據(jù),例如報名,提交一個表單之類的。這種簡單的需求用 LeanCloud 來實(shí)現(xiàn)是非常方便的。不用寫后端代碼,不用部署后端應(yīng)用,把運(yùn)營人員的賬號加入到協(xié)作,他們就可以隨時查看數(shù)據(jù)了,甚至都不用寫管理界面了。 美中不足的是,LeanCloud 的數(shù)據(jù)存儲管理界面面向的用戶群體還是以開發(fā)者為主,運(yùn)營的同學(xué)用起來會不太順手。 另外一個需求就是導(dǎo)出數(shù)據(jù)到 Excel,運(yùn)營同學(xué)對于 Excel 的使用還是得心應(yīng)手的。 于是就有了這個工具: https://github.com/ROYL-Design/leanstorage-exporter Demo 放在 Github Pages 上: https://royl-design.github.io/leanstorage-exporter/ 可以查詢 LeanStorage 的數(shù)據(jù),并且導(dǎo)出為 Excel、CSV 等格式的文件。 你可以直接使用 Demo,或者部署在本地,又或者自己的服務(wù)器上。 使用 Vue.js + SheetJS,100%前端實(shí)現(xiàn),100%開源,無需擔(dān)心數(shù)據(jù)風(fēng)險。 希望可以幫到大家。
簡單地說我們發(fā)布了一個在客戶端訂閱滿足特定條件的數(shù)據(jù)變化的 API,詳情見: https://blog.leancloud.cn/5916/
這對于需要實(shí)現(xiàn)實(shí)時協(xié)作、實(shí)時通知等功能的應(yīng)用很有用。
申請 cdn 加速需要開通商業(yè)版,有備案域名,如果是 iPhone 應(yīng)用,最好開通 https。這里有申請郵箱: https://forum.leancloud.cn/t/cdn/14318 LeanCloud 的備案流程大概是:初審-申請幕布-上傳照片-郵寄資料-復(fù)審-提交 其實(shí)你在上海或北京的話,初審后有個地址你直接去就行,我去的是 Ucloud。然后直接到復(fù)審,會省掉不少時間。 備案的同時申請 ssl 證書,我用的是 https://www.aliyun.com/product/cas 免費(fèi)版走了不少彎路,折騰了半個多月,希望后來者不要踩坑。
leancloud 測試并發(fā)如下, 帶數(shù)據(jù)庫查詢: Running 10s test @ http://xxxx.leanapp.cn/?id=58b62dfa61ff4b006ccc0113 12 threads and 100 connections Thread Stats Avg Stdev Max +/- Stdev Latency 172.36ms 159.26ms 1.37s 80.84% Req/Sec 54.37 21.85 128.00 63.17% 6459 requests in 10.09s, 9.06MB read Non-2xx or 3xx responses: 4756 Requests/sec: 640.04 Transfer/sec: 0.90MB
阿里云的并發(fā)測試, 1 核 1G 最低配, 不帶數(shù)據(jù)庫查詢: Running 10s test @ http://xxx.76.215.104:3000/ 12 threads and 100 connections Thread Stats Avg Stdev Max +/- Stdev Latency 312.36ms 139.82ms 1.89s 92.13% Req/Sec 21.15 11.70 70.00 63.86% 2007 requests in 10.10s, 0.94MB read Socket errors: connect 0, read 0, write 0, timeout 43 Requests/sec: 198.80 Transfer/sec: 95.13KB
0.5 CPU 具體是什么配置
RT
最近 leancloud 調(diào)整了收費(fèi)政策,要么用免費(fèi)但資源少的開發(fā)版,要么每個月 900 用商業(yè)版~ 很奇怪為什么不再增加一個彈性計(jì)算版?@LeanCloud
所以在此問問大家有沒有類似的平臺推薦?或者源碼也是極好的。
要求不多,有用戶管理以及數(shù)據(jù)存儲功能即可(包括數(shù)據(jù)存取時的用戶權(quán)限控制
謝謝大家??
自 2016 年 7 月 5 日周二零時起,通過 LeanCloud 發(fā)送國內(nèi)文本短信的價格將從每條 0.06 元下調(diào)至 0.05 元,降幅達(dá)到 17%。
這是在我們與渠道服務(wù)商的共同努力下進(jìn)一步優(yōu)化了通道成本的結(jié)果,我們愿意讓 LeanCloud 用戶享受由此帶來的價格優(yōu)惠。
短信發(fā)送記錄可以在 應(yīng)用控制臺 > 消息 > 短信 中查詢,短信費(fèi)用會在郵件賬單中列出。如果有任何疑問,請聯(lián)系 [email?protected] 。
不是很了解 LeanCloud 的開發(fā)者經(jīng)常會問「 LeanCloud 與已有的很多云服務(wù)有什么區(qū)別呢?」下面我們就以國內(nèi)比較有代表性的阿里云為例,跟我們做下對比。
產(chǎn)品的區(qū)別
進(jìn)入阿里云網(wǎng)站可以看到阿里云的產(chǎn)品介紹。產(chǎn)品列表有彈性計(jì)算、數(shù)據(jù)庫、存儲與 CDN 、網(wǎng)絡(luò)、大規(guī)模計(jì)算、云盾、管理與監(jiān)控、應(yīng)用服務(wù)、互聯(lián)網(wǎng)中間件、移動服務(wù)、域名與網(wǎng)站等,每個選項(xiàng)下面又有非常多的子產(chǎn)品列表,提供的服務(wù)種類繁多。個人感覺幾乎開發(fā)中需要使用的服務(wù)器產(chǎn)品,阿里云應(yīng)該都提供了。這些產(chǎn)品更偏向于較底層的服務(wù),用戶要想使用起來需要具備一定的能力。
LeanCloud 則完全不同。它提供了四項(xiàng)產(chǎn)品,分別是 LeanStorage (數(shù)據(jù)、文件存儲及云引擎)、 LeanMessage (短信、推送及實(shí)時通訊服務(wù))、 LeanAnalytics (統(tǒng)計(jì)分析服務(wù))、 LeanModules (各種其他通用組件)。看起來很精簡卻有些抽象,那這些產(chǎn)品具體又能滿足什么需求呢?
概念上的區(qū)別
阿里云提供的是類似于 AWS (亞馬遜的云服務(wù))一樣的傳統(tǒng)云服務(wù)。使用了阿里云你就不用再去操心那些與硬件和底層運(yùn)維相關(guān)的事情,比如硬盤損壞、主機(jī)托管、服務(wù)器配置網(wǎng)絡(luò)等等。
但如果想要開發(fā)一個自己的 App ,你仍然需要在阿里云上購買機(jī)器,選擇部署到哪個機(jī)房,還要購買數(shù)據(jù)庫,選擇數(shù)據(jù)具體是怎樣的規(guī)格,然后還要對這臺機(jī)器進(jìn)行完整的配置。雖然比沒有云服務(wù)的日子已經(jīng)輕松了許多,但這些操作仍然需要一個專業(yè)的工程師才能很好地完成。
而使用 LeanCloud 用戶卻不需要操心這些事情,可以說基本上不用考慮服務(wù)器的細(xì)節(jié)。
LeanCloud 提供的是 BaaS 服務(wù)( Backend as a Service 后端即服務(wù)),又被稱為云服務(wù) 2.0 。簡而言之,云服務(wù) 1.0 解決的是不再讓你擔(dān)心服務(wù)器,而 BaaS 的目標(biāo)是幫你解決全部服務(wù)器運(yùn)維,甚至是部分后端業(yè)務(wù)邏輯。那 LeanCloud 究竟是怎么做到的呢?回答這個問題之前,我們看下一個 App 一般都是什么樣子。
以 LeanCloud 的用戶「懂球帝」為參考,不論什么產(chǎn)品基本上都需要一套賬號系統(tǒng),目前較通用的做法是使用手機(jī)號碼注冊,發(fā)送短信驗(yàn)證;基于這個賬號還要存儲一些數(shù)據(jù)項(xiàng),如昵稱、頭像等信息,再到真正的主業(yè)務(wù)邏輯,需要通過服務(wù)器基于某個邏輯運(yùn)算出結(jié)果交給客戶端做展示。
那么我們再考慮一個問題,為什么我們每次做一個產(chǎn)品都要反反復(fù)復(fù)地開發(fā)這些差不多一樣的邏輯呢?比如賬號系統(tǒng)、數(shù)據(jù)存儲、短信驗(yàn)證、郵件驗(yàn)證、推送服務(wù)甚至是即時聊天,有沒有辦法讓這些東西拿來就用,讓自己能夠最快速地投入開發(fā)呢?當(dāng)然有辦法,這就是 LeanCloud 所做的事情。
具體使用 LeanCloud
來舉個具體例子。在 LeanCloud 中想要實(shí)現(xiàn)一套賬號系統(tǒng)共分為三步:注冊賬號、創(chuàng)建一個應(yīng)用、下載對應(yīng)的 SDK 。就這三步?難道不用寫代碼嗎?是的,不用寫代碼你其實(shí)已經(jīng)擁有了一套支持 ACL (訪問權(quán)限控制)、支持短信驗(yàn)證注冊、支持郵件注冊這樣具備完整安全體系的賬號系統(tǒng),客戶端工程師只需直接使用即可。例如, Web 前端通過 JavaScript SDK 在瀏覽器使用賬戶系統(tǒng),具體代碼如下: // 創(chuàng)建一個實(shí)例 const user = new AV.User(); // 設(shè)置用戶名 user.set('username', 'wangxiao); // 設(shè)置密碼 user.set('password', 123456789); // 注冊 user.signUp().then(user => { // 注冊成功 }, error => { // 注冊失敗 });
再如經(jīng)常被使用的短信驗(yàn)證功能,你不需要去找服務(wù)端工程師去開發(fā)一個專用接口,而是直接在瀏覽器中調(diào)用 JavaScript SDK 的方法(支持模板來定制短信內(nèi)容),具體代碼如下: // 發(fā)送手機(jī)驗(yàn)證碼 AV.Cloud.requestSmsCode({ mobilePhoneNumber: '182xxxx5548' }).then(() => { // 發(fā)送成功 }, error => { // 發(fā)送失敗 }); // 校驗(yàn)驗(yàn)證碼 AV.Cloud.verifySmsCode('1234', '182xxxx5548') .then(() => { // 驗(yàn)證成功 }, error => { // 驗(yàn)證失敗 }); // 短信模板 AV.Cloud.requestSmsCode({ mobilePhoneNumber: '182xxxx5548', template: 'Template_Name', ttttName: '自定義模板變量名' }).then(() => { // 發(fā)送成功 }, error => { // 發(fā)送失敗 });
短信驗(yàn)證僅僅是我們所開放的眾多功能中的一項(xiàng),你還可以使用 SDK 輕松實(shí)現(xiàn)數(shù)據(jù)存儲、文件存儲( CDN )、推送、即時聊天等實(shí)用功能。如此以來你的開發(fā)效率會大幅提升,服務(wù)器端對于你來說完全是透明的,這樣就能把所有精力集中到研發(fā)核心產(chǎn)品上去,而后續(xù)的數(shù)據(jù)運(yùn)營和管理工作可以直接在 LeanCloud 的控制臺中進(jìn)行,甚至在初期你都不需要給運(yùn)營人員編寫一個對應(yīng)的管理后臺。
成本的區(qū)別
選擇傳統(tǒng)的云服務(wù),你可能需要更多地去了解服務(wù)端的結(jié)構(gòu),要綜合考慮在云服務(wù)上搭建出一套自己的系統(tǒng)所付出的成本,還需要找到合適的工程師去維護(hù)這些服務(wù),找到后端工程師來開發(fā)服務(wù)端很多通用的業(yè)務(wù)邏輯。
如果使用 LeanCloud 這些事情都不用去考慮,直接使用相應(yīng)的服務(wù)即可。同時我們的云服務(wù)按照使用量計(jì)費(fèi),并提供了一定額度的免費(fèi)使用量,在初期用戶量少的時候基本不會產(chǎn)生什么費(fèi)用,只有當(dāng)用戶量增長到一定量級時才會產(chǎn)生相應(yīng)的費(fèi)用??傊褂?LeanCloud 不僅僅省去了后期運(yùn)維的成本,還減少了后端工程師的工作量,加速產(chǎn)品迭代。
「壹期壹問」 VOL.4 收錄的問題
真正優(yōu)質(zhì)的代碼具備什么條件?如何高效的編寫優(yōu)質(zhì)代碼?——來自用戶 yuklng
來自 LeanCloud iOS 開發(fā)工程師陳宜龍的回答
真正優(yōu)質(zhì)的代碼具備什么條件? 優(yōu)質(zhì)的代碼基本的條件:輕量級、低耦合(松耦合)、易替換、易刪除。
什么叫低耦合?
你如果可以從你的代碼中刪除某一模塊而不用因此去重寫其他模塊的話,你的代碼就通常被稱為是低耦合的。
我們都喜歡輕量級的框架,因?yàn)槟銘?yīng)該時刻保持著這樣的警覺:遲早有一天,這個庫、這坨代碼會被替換、移除掉。
這可能讓你聯(lián)想起了 iOS 領(lǐng)域一個教科書級別的事件—— ASI 切換 AFN 。 ASI 曾經(jīng)是 iOS 開發(fā)首選的第三方網(wǎng)絡(luò)框架,后來作者宣布停止維護(hù),開發(fā)者紛紛開始遷移到 AFN 。它教會我們一個道理:
讓你的代碼「易替換、易刪除」,第三方框架代碼質(zhì)量是一方面,使用者也有責(zé)任。
如何高效的編寫優(yōu)質(zhì)代碼?
想寫優(yōu)質(zhì)代碼,首先要敢于寫垃圾代碼。
正如程序員心中的上古巨神 Alen Jay Perlis 在 《 Epigrams on Programming 編程警句》 所說: Everything should be built top-down, except the first time. 凡事都應(yīng)該高屋建瓴,除非你是第一次干。
所以,在 beta 版本里犯錯,上帝都會原諒。
不要在開始寫一個應(yīng)用之前就去想著能寫一個萬全的架構(gòu)。道理很簡單,就像你在早上 8:00 出門上班前許愿「一路綠燈」,即使當(dāng)時愿望實(shí)現(xiàn)了, 8:00 全北京的燈都變綠了,但等你到下個路口,一樣還是該紅燈時紅燈。我們是很難預(yù)先猜測,但我們卻可以在發(fā)生小變化時,就及早去想辦法應(yīng)對發(fā)生更大變化的可能。也就是說,等到變化發(fā)生時立即采取行動。
第一次的時候盡管大膽的去寫一堆亂七八糟的代碼。
但垃圾代碼始終還是要轉(zhuǎn)換成優(yōu)質(zhì)代碼的,如何轉(zhuǎn)換?遵循「開放-封閉原則」,即 The Open-Closed Principle (簡稱 OCP )去轉(zhuǎn)換: 這個原則其實(shí)是有兩個特征,一個是說 「對于擴(kuò)展是開放的, Open for extension 」,另一個是說 「對于更改是封閉的, Closed for modification 」。我們在做任何項(xiàng)目的時候,都不要指望項(xiàng)目一開始時需求確定就再也不會變化,這是不現(xiàn)實(shí)也不科學(xué)的想法,而既然需求是一定會變化的,那么如何在面對需求的變化時,設(shè)計(jì)的軟件可以相對容易修改,不至于說新需求一來,就要把整個程序推倒重來。
上面是《大話設(shè)計(jì)模式》中的一段話,書中也給出了一個例子: 比如你在寫一個加法程序,你很快在一個 client 類中就完成。此時變化還沒有發(fā)生。然后我讓你加一個減法功能,你發(fā)現(xiàn),增加功能需要修改原來這個類,這就違背了「開放-封閉原則」,于是你就該考慮重構(gòu)程序,增加一個抽象的運(yùn)算類,通過一些面向?qū)ο蟮氖侄?如繼承、多態(tài)等來隔離具體加法和減法與 client 耦合,需求依然可以滿足,還能應(yīng)對變化。這時我又要你再加乘除法功能,你就不需要再去更改 client 以及加法減法的類了,而是增加乘法和除法子類就可。即面對需求,對程序的改動是通過增加新代碼進(jìn)行的,而不是更改現(xiàn)有的代碼這就是「開放-封閉原則」的精神所在。
你應(yīng)該大膽嘗試,在每一次嘗試時都開新的坑,去犯新的錯,然后通過迭代慢慢來完善。成為一個專業(yè)的軟件開發(fā)者的過程就是不斷積累后悔和「 error check list 」 的過程。你從「 Run Success 」身上學(xué)不到任何東西,知道優(yōu)質(zhì)代碼長什么樣,作用也微乎其微,你需要的是:對垃圾代碼記憶猶新。
Perlis 也說過: In programming, as in everything else, to be in error is to be reborn.
同時敢不敢寫垃圾代碼,完全取決于心態(tài)。寫程序其實(shí)有兩種心態(tài),一種是做 Demo 的心態(tài),一種是做項(xiàng)目的心態(tài)。讓我們來對比下這兩個心態(tài):
對于業(yè)務(wù)邏輯,垃圾代碼有時候是更好的選擇,否則極小的封裝都可以將你再次帶入「過度封裝」的陷阱。業(yè)務(wù)邏輯是那種有著無盡的 if else 、邊界情況和快速而 dirty 的 hack 的代碼。
現(xiàn)在你再回過頭來思考下:一大坨垃圾代碼,和過度封裝的業(yè)務(wù)邏輯代碼,哪個更好?
有時候刪掉一個大的錯誤比刪掉 18 個彼此相關(guān)聯(lián)的小錯誤更為容易。
摒棄代碼潔癖,擁抱合理的冗余
冗余一樣被貼著「垃圾代碼」的標(biāo)簽,難道冗余就沒有一點(diǎn)好處?你必須試著擁抱合理的冗余。
我們會通過復(fù)制和粘貼來將部分代碼重復(fù)使用以避免引入依賴性,提高靈活度,但代價就是冗余。
「一坨代碼被使用兩次,就應(yīng)該封裝」這個論調(diào)不應(yīng)該被提倡。往往是剛開始很方便,越往后,隨著需求的變更,越成為一種累贅。 為了一個小功能就去寫一個庫,甚至這個庫只有一個方法,這樣會帶來什么問題?你削減了使用到了這個庫的所有模塊之間的獨(dú)立性,這樣做毫無必要。
想寫優(yōu)質(zhì)代碼,要學(xué)會如何不寫代碼
代碼越少, Bug 越少,代碼也就越優(yōu)質(zhì)。
正如 iOS Architecture Patterns 這篇博客所言: The less code you have, the less bugs you have. The best code is the code that has never been written.
你應(yīng)該始終明白什么是最優(yōu)雅的解決方式。
為第三方庫寫工具類、模板類 我們構(gòu)建模塊不是為了復(fù)用,而是為了易于修改。
建工具類,出于兩個完全相反的原因:低耦合、緊耦合。
低耦合
上面提到的「 ASI 切換 AFN 」教會我們:要為第三方庫封裝。盡量不要直接使用第三方庫,而是為它們封裝一層,通常我們叫這些類 Tool 、 Handler 、 Helper 、 Manager (先不吐槽命名了)??紤]到的是替換需求,由第三方庫而衍生出來的工具類,通常也放在叫 Tool 的文件夾下。這樣做的目的是盡可能將變化頻繁的部分和相對更穩(wěn)定的部分隔開來實(shí)現(xiàn)低耦合。這也同樣適用于數(shù)據(jù)庫以及各種可能會變更的 UI 組件。
緊耦合
但也不要一味追求「低耦合」,要根據(jù)實(shí)際情況來選擇「緊耦合 tight coupling 」。比如錯誤處理就是一個需要與項(xiàng)目緊密結(jié)合在一起的操作。這個緊耦合的部分,我們就要放到 Tool 里,這也是建 Tool 的另一要考慮的原因:庫總是試圖迎合所有的需求,而我們只會用到其中小部分功能,而且只會對相應(yīng)的響應(yīng)做出特定的處理。所以建工具類可以隱藏不必要的細(xì)節(jié)。
好的 API 總是大而全,而建 Tool 則是我們意識到我們不能同時讓所有人都高興。 開發(fā)一個好用的 API 和開發(fā)一個具有擴(kuò)展性的 API 通常是互相沖突的。 Tool 的作用就是讓他們好用。
造輪子
你在建 Tool 時應(yīng)該已經(jīng)體會到緊耦合的好處了,但有時 Tool 已經(jīng)完全滿足不了我們的緊耦合需求,這時我們就需要造輪子。
iOS 里 Star 數(shù)最多的一個庫是 AFNetworking ,為什么?當(dāng)年 iOS 5 、 iOS 6 時代還沒有 NSURLSession 的時候, NSURLConnection 做斷點(diǎn)續(xù)傳和斷點(diǎn)下載是非常困難的,而且蘋果的 API 不對外隱藏任何細(xì)節(jié),這也就造就了 AFNetworking ——它為大家隱藏了很多細(xì)節(jié),所以好用。
同樣的道理,在日益復(fù)雜化的 App 開發(fā)中, Auto layout 逐漸成為最佳選擇。但復(fù)雜的 VFL 語法,以及開發(fā)者不太買賬的 Storyboard 、 XIB 可視化操作,也成就了 Masorny 。 Masorny 以其優(yōu)雅的鏈?zhǔn)郊兇a操作,漸漸成為自動布局的主流框架。
設(shè)計(jì)模式的「單一責(zé)任」原則告訴我們: 每一個模塊都應(yīng)該只去解決一個難題
但我們更需要: 每一個難題都只應(yīng)該由一個模塊去解決
當(dāng)一個問題需要兩個模塊去做的時候,通常都是因?yàn)楦淖円徊糠中枰硗庖徊糠值母淖儭?
一個寫得很糟糕但是有著簡單接口的組件,通常比需要互相協(xié)調(diào)的兩個組件更容易使用。
你應(yīng)該像 AFNetworking 、 Masorny 那樣,寫出這樣的優(yōu)質(zhì)代碼: 能將寫起來、維護(hù)起來,或者刪除起來最困難的部分互相隔離開。
如何參與「壹期壹問」?
大家可以通過 問題提交通道 來向 LeanCloud 提問。
我是特地來點(diǎn)個贊的。 Leanote 是個讓我眼前一亮的企業(yè) Knowledge base 解決方案。開源后簡直是大大方便了我們!同時Leanote.com 由于具備 Markdown 書寫私有技術(shù)文檔,以及一鍵公開為博客的功能,現(xiàn)在已經(jīng)成為我的主力寫作平臺。感謝作者!
想統(tǒng)一 iOS/Android 推送,但是這樣好捉急。。。
原本的目的是移植一個模型到安卓,遇到問題后,重新做了個簡單的模型驗(yàn)證,出現(xiàn)同樣的問題。
python 訓(xùn)練的代碼 model = keras.Sequential([keras.layers.Dense(units=1, input_shape=[1])]) model.compile(optimizer='sgd', loss='mean_squared_error') xs = np.array([-1.0, 0.0, 1.0, 2.0, 3.0, 4.0], dtype=np.float32) ys = np.array([-3.0, -1.0, 0.0, 3.0, 5.0, 7.0], dtype=np.float32) model.fit(xs, ys, epochs=500) keras_file = 'linear.h5' keras.models.save_model(model, keras_file)
轉(zhuǎn)換成 .tflite 后,在安卓使用 Interpreter interpreter = new Interpreter(FileUtil.loadMappedFile(activity, "linear.tflite")); interpreter.allocateTensors(); int probabilityTensorIndex = 0; int[] probabilityShape = interpreter.getOutputTensor(probabilityTensorIndex).shape(); // DataType probabilityDataType = interpreter.getOutputTensor(probabilityTensorIndex).dataType(); TensorBuffer outputProbabilityBuffer = TensorBuffer.createFixedSize(probabilityShape, probabilityDataType); int inputTensorIndex = 0; DataType inputDataType = interpreter.getInputTensor(inputTensorIndex).dataType(); int[] inputShape = interpreter.getInputTensor(inputTensorIndex).shape(); TensorBuffer inputBuffer = TensorBuffer.createFixedSize(inputShape, inputDataType); final float[] input = {10}; inputBuffer.loadArray(input); interpreter.run(inputBuffer, outputProbabilityBuffer);
報錯是 I/tflite: Initialized TensorFlow Lite runtime. E/AndroidRuntime: FATAL EXCEPTION: inference Process: com.example.my1application, PID: 26839 java.lang.IllegalArgumentException: DataType error: cannot resolve DataType of org.tensorflow.lite.support.tensorbuffer.TensorBufferFloat at org.tensorflow.lite.Tensor.dataTypeOf(Tensor.java:344) at org.tensorflow.lite.Tensor.throwIfTypeIsIncompatible(Tensor.java:397) at org.tensorflow.lite.Tensor.getInputShapeIfDifferent(Tensor.java:287) at org.tensorflow.lite.NativeInterpreterWrapper.run(NativeInterpreterWrapper.java:137) at org.tensorflow.lite.Interpreter.runForMultipleInputsOutputs(Interpreter.java:316) at org.tensorflow.lite.Interpreter.run(Interpreter.java:277) at com.example.my1application.DisplayMessageActivity$1.run(DisplayMessageActivity.java:114) at android.os.Handler.handleCallback(Handler.java:815) at android.os.Handler.dispatchMessage(Handler.java:104) at android.os.Looper.loop(Looper.java:207) at android.os.HandlerThread.run(HandlerThread.java:61)
背景:最近個人 project 需要做了一個方言的機(jī)器學(xué)習(xí)分類器,目前就兩種,輸入是視頻的音頻,模型是最簡單的 1dCNN 只有幾層,數(shù)據(jù)量大約有 30 小時,兩種方言對半分。在測試集上準(zhǔn)確率都在 80%以上,偶爾飆升到 90%多。但實(shí)際場景的話,有一種方言被嚴(yán)重多估大概一半。
分析:第一點(diǎn)大概是數(shù)據(jù)不夠,在 CNN 下面加 BatchnNormalization 的話準(zhǔn)確率很低,所以或稱了 dropout 效果就好很多了。但大概也是如此,因?yàn)槿斯?biāo)注的數(shù)據(jù)太少,導(dǎo)致實(shí)際場景準(zhǔn)確率沒有測試集高,而且都是某一種方言錯得特別多,另一種準(zhǔn)確率則不錯。第二點(diǎn)大概是模型問題,因?yàn)槲沂菢I(yè)余的,所以也懂得不多。第三是提取特征的問題,音頻的分析用的是 librosa,提取 mfcc/zero_crossing/tempogram/bandwidth 等特征。我猜更好可能是先要分離背離音,然后再分析比較好。
問題:目前優(yōu)化怎樣會比較好?增加數(shù)據(jù)量?嘗試在分離特征上努力,分離背景音?只選沒有背景音的輸入數(shù)據(jù)?優(yōu)化模型?謝。
搬運(yùn)自我的知乎: https://zhuanlan.zhihu.com/p/142667683
這段時間計(jì)算機(jī)視覺領(lǐng)域出現(xiàn)了一些使用 Julia 開源的 相關(guān)工作 ,要科學(xué)合理地對比這些相關(guān)工作,儲備新的煉丹技巧,筆者 不得不開始熟悉 Julia 。筆者從周一拿到 Julia 文檔開始,這周的試驗(yàn)都是使用 Julia 完成的。這里,打算先說一說筆者的幾個感受,幫助大家判斷一下自己是否需要著手入坑這門語言:
實(shí)用性 :★★★★☆
兩三年前研究 運(yùn)籌學(xué) 的時候用 Julia 做最優(yōu)化問題,感覺 比 Cplex 、Matlab 好用。近兩年 Julia 開源的 深度學(xué)習(xí) 工作逐漸 增多 ,研究的一般是 基本問題 ,在 toy 數(shù)據(jù)集 上跑試驗(yàn)。近期也出現(xiàn)了一些 CV 領(lǐng)域的項(xiàng)目。
生態(tài) :★★★☆☆
深度學(xué)習(xí)庫 Flux 和 GPU 計(jì)算庫 CuArray 基本穩(wěn)定下來 ,周邊項(xiàng)目更新迅速,比如常用的預(yù)訓(xùn)練模型也都可以在 Julia 社區(qū)中找到靠譜的庫了(如 MetalHead )。當(dāng)然, 周邊項(xiàng)目 的快速迭代也會導(dǎo)致一些庫 動不動就報錯 (甚至在安裝時都要費(fèi)一番功夫)。另外比較有特點(diǎn)的是,大部分常用的 Python 庫都有 PyCall 封裝 的跟進(jìn),實(shí)在不行自己用 PyCall 、JavaCall 、Clang 寫個 膠水層 也能用。
易用性 :★★★★★
Julia 的語法真的很 簡單 ,混合了 Python 和 Matlab, 30 分鐘入門 后續(xù)查漏補(bǔ)缺即可。Julia 內(nèi)置了大量的科學(xué)計(jì)算方法(符號),確實(shí)比 Python 直觀 和好寫了很多。美中不足的是社區(qū)現(xiàn)有的代碼和 官方最佳實(shí)踐比較少 ,筆者正在試圖在這方面貢獻(xiàn)一些工作。
運(yùn)行速度 :★★☆☆☆
運(yùn)行速度比 PyThon 稍有提高 ,但是第一次運(yùn)行需要編譯因此 調(diào)試時體驗(yàn)稍差于 Python 。多線程跑崩過系統(tǒng),GPU 的分布式框架還不太完善。
一、裝機(jī)必備
在開始之前推薦一些裝機(jī)必備??紤]到同學(xué)們比較熟悉 Python 因此使用 Python 中的 toolbox 進(jìn)行類比,懶癌患者可以直接裝推薦安裝的部分: Julia 對應(yīng) Python Pkg3 對應(yīng) pip3 JuliaPro 對應(yīng) Anaconda (推薦直接安裝這個) IJulia + Jupyter 對應(yīng) IPython + Jupyter (推薦使用) VSCode Julia 插件 對應(yīng) VSCode Python 插件 PkgMirrors + 浙大源 對應(yīng) 清華源(推薦使用)
二、煉丹示例
Julia 的語言 Feature 較多 ,但都 比較通俗 。因此筆者比較推薦同學(xué)們 在使用過程中 慢慢 熟悉 (就算你想先 慢慢學(xué) 一個月再去做實(shí)驗(yàn) 老板也不同意 是吧)。如果你實(shí)在想先瀏覽一下基礎(chǔ)語法,筆者總結(jié)了一個 Notebook ,幫助你在 15 分鐘內(nèi)看完并有一個大概印象。
下面筆者總結(jié)了 Julia 版的常用 Pipeline,可以幫助同學(xué)們理解如何像用 Python + PyTorch 一樣簡單地使用 Julia 完成深度學(xué)習(xí)項(xiàng)目。在做實(shí)驗(yàn)的時候同學(xué)們可以簡單復(fù)制粘貼,修修改改先跑上。(逃
1. MLP + MNIST 實(shí)現(xiàn)一個最小用例
首先,我們先完成一個最小用例,實(shí)現(xiàn)在 GPU 上訓(xùn)練一個多層感知器擬合 MNIST,了解基本操作。由于篇幅限制,完整代碼請參考并運(yùn)行 MLP+MNIST 。
Flux 是 Julia 中的深度學(xué)習(xí)庫,其完全由 Julia 實(shí)現(xiàn),結(jié)構(gòu)輕量化,是 Julia 中的 PyTorch 。因此首先導(dǎo)入 Flux 備用模型定義和反向傳播(訓(xùn)練)。 # 從 Flux 中引入所需組件 using Flux, Flux.Data.MNIST, Statistics using Flux: onehotbatch, onecold, crossentropy, throttle, params
盡管 Flux 中目前已經(jīng)實(shí)現(xiàn)了 gpu() 方法,但功能有限。所幸 Flux 在 GPU 上的功能基于 CuArrays 實(shí)現(xiàn),可以使用 CUDAapi, CUDAdrv, CUDAnative 來設(shè)置 Flux 使用哪個 GPU,或是只使用 CPU 。 using CUDAapi, CUDAdrv, CUDAnative gpu_id = 1 ## set < 0 for no cuda, >= 0 for using a specific device (if available) if has_cuda_gpu() && gpu_id >=0 device!(gpu_id) device = Flux.gpu @info "Training on GPU-$(gpu_id)" else device = Flux.cpu @info "Training on CPU" end
另外,Flux 目前仍不支持分布式 GPU 訓(xùn)練,要想實(shí)現(xiàn)該功能也需要利用上述庫寫 scatter 和 gather 手動實(shí)現(xiàn)。
與 PyTorch 相同,Flux 定義了一個開箱即用的數(shù)據(jù)集 MNIST 。這里我們調(diào)用 MNIST.images() 和 MNIST.labels() 加載數(shù)據(jù)集和對應(yīng)的 label,并使用 Flux 中提供的 onehotbatch 對 label 進(jìn)行 onehot 編碼。 imgs = MNIST.images() labels = onehotbatch(MNIST.labels(), 0:9)
目前,Flux 沒有提供數(shù)據(jù)集切分的函數(shù),因此我們需要手動進(jìn)行該過程。具體而言,我們使用 partition 對加載進(jìn)來的數(shù)據(jù)集進(jìn)行切分,將每 1000 張圖像分為一個 batch,并使用 |> device (遍歷每個元素分別執(zhí)行上文中定義的 device())全部圖像遷移到 GPU 中。 train = [(cat(float.(imgs[i])..., dims = 4), labels[:,i]) for i in partition(1:60_000, 1000)] |> device
同樣,我們選擇數(shù)據(jù)集中前 1000 張圖片作為測試數(shù)據(jù)集,也遷移到 GPU 中。 test_X = cat(float.(MNIST.images(:test)[1:1000])..., dims = 4) |> device test_y = onehotbatch(MNIST.labels(:test)[1:1000], 0:9) |> device
Flux 中的模型定義與 PyTorch 相似,Chain 取代了 nn.Sequential,Conv/MaxPool/Dense 等 layer 也已經(jīng)封裝好(封裝的 cuDNN )可以直接調(diào)用。如下所示,定義模型、損失函數(shù)和評估方法只需要三段代碼。 model = Chain( Conv((2,2), 1=>16, relu), MaxPool((2, 2)), Conv((2,2), 16=>8, relu), MaxPool((2, 2)), x -> reshape(x, :, size(x, 4)), Dense(288, 10), softmax ) |> device loss(x, y) = crossentropy(model(x), y) accuracy(x, y) = mean(onecold(model(x)) .== onecold(y))
Flux 為使用者提供了 Adam 優(yōu)化器,相比于 PyTorch 的版本,該 Adam 優(yōu)化器似乎對學(xué)習(xí)旅更為敏感。如果遇到不收斂的情況可以嘗試降低 LR 。后續(xù)打算對其 FLux 和 PyTorch 的優(yōu)化器。和 PyTorch 相似,我們直接使用 ADAM(LR),定義優(yōu)化器,使用 train!() 進(jìn)行訓(xùn)練。 opt = ADAM(0.01) evalcb() = @show(accuracy(test_X, test_y)) epochs = 5 for i = 1:epochs Flux.train!(loss, Flux.params(model), train, opt) end
值得注意的是 Flux 中構(gòu)建的圖也為動態(tài)圖,無需考慮計(jì)算圖的構(gòu)建,直接定義所需的計(jì)算操作就可以了。
進(jìn)行推斷時也如同 Pytorch,可以直接調(diào)用模型。如下,從測試集中選擇一張圖片放入模型,預(yù)測所屬類別。 using Colors, FileIO, ImageShow img = test_X[:, :, 1:1, 7:7] println("Predicted: ", Flux.onecold(model(img |> device)) .- 1) save("outputs.jpg", collect(test_X[:, :, 1, 7]))
2. VGG + Cifar 封裝常用方法 Finetune 模型
在試驗(yàn)和競賽中,我們通常要對讀入圖像進(jìn)行增廣;模型也通常是基于某個 pretrained 的模型 Finetune 的,因此接下來我們看如何對這些內(nèi)容進(jìn)行封裝。由于篇幅限制,這里只說明重要部分,完整代碼請參考并運(yùn)行 VGG+Cifar10 。
目前 Flex 和周邊的生態(tài)還不太完善,圖像增強(qiáng)部分的實(shí)現(xiàn)實(shí)屬有限。這里我們參照 pytorch 實(shí)現(xiàn)最基本的圖像增廣的預(yù)處理過程。更為豐富的預(yù)處理恐怕只能自己編寫或是等待官方更新,當(dāng)然,這也是重新造輪子的好機(jī)會~ function resize_smallest_dimension(im, len) reduction_factor = len/minimum(size(im)[1:2]) new_size = size(im) new_size = ( round(Int, size(im,1)*reduction_factor), round(Int, size(im,2)*reduction_factor), ) if reduction_factor < 1.0 # Images.jl's imresize() needs to first lowpass the image, it won't do it for us im = imfilter(im, KernelFactors.gaussian(0.75/reduction_factor), Inner()) end return imresize(im, new_size) end # Take the len-by-len square of pixels at the center of image `im` function center_crop(im, len) l2 = div(len,2) adjust = len % 2 == 0 ? 1 : 0 return im[div(end,2)-l2:div(end,2)+l2-adjust,div(end,2)-l2:div(end,2)+l2-adjust] end function preprocess(im) # Resize such that smallest edge is 256 pixels long im = resize_smallest_dimension(im, 256) # Center-crop to 224x224 im = center_crop(im, 224) # Convert to channel view and normalize (these coefficients taken # from PyTorch's ImageNet normalization code) μ = [0.485, 0.456, 0.406] # the sigma numbers are suspect: they cause the image to go outside of 0..1 # 1/0.225 = 4.4 effective scale σ = [0.229, 0.224, 0.225] #im = (channelview(im) .- μ)./σ im = (channelview(im) .- μ) # Convert from CHW (Image.jl's channel ordering) to WHCN (Flux.jl's ordering) # and enforce Float32, as that seems important to Flux # result is (224, 224, 3, 1) #return Float32.(permutedims(im, (3, 2, 1))[:,:,:,:].*255) # why return Float32.(permutedims(im, (3, 2, 1))[:,:,:,:]) end
這里將 MNIST 的數(shù)據(jù)集切分方法進(jìn)行封裝,使用 get_processed_data 和 get_test_data 構(gòu)建訓(xùn)練集合、驗(yàn)證集合和測試集合。 using Metalhead: trainimgs using Images, ImageMagick function get_processed_data(args) # Fetching the train and validation data and getting them into proper shape X = trainimgs(CIFAR10) imgs = [preprocess(X[i].img) for i in 1:40000] #onehot encode labels of batch labels = onehotbatch([X[i].ground_truth.class for i in 1:40000],1:10) train_pop = Int((1-args.splitr_)* 40000) train = device.([(cat(imgs[i]..., dims = 4), labels[:,i]) for i in partition(1:train_pop, args.batchsize)]) valset = collect(train_pop+1:40000) valX = cat(imgs[valset]..., dims = 4) |> device valY = labels[:, valset] |> device val = (valX,valY) return train, val end function get_test_data() # Fetch the test data from Metalhead and get it into proper shape. test = valimgs(CIFAR10) # CIFAR-10 does not specify a validation set so valimgs fetch the testdata instead of testimgs testimgs = [preprocess(test[i].img) for i in 1:1000] testY = onehotbatch([test[i].ground_truth.class for i in 1:1000], 1:10) |> device testX = cat(testimgs..., dims = 4) |> device test = (testX,testY) return test end
Julia 中預(yù)訓(xùn)練模型庫正蓬勃發(fā)展,比較成熟的有 Metalhead (類似于 Torchvision )等。這里我們使用 Metalhead 中提供的模型結(jié)構(gòu)和預(yù)訓(xùn)練參數(shù)構(gòu)建 VGG19,并替換后面的層完成當(dāng)前任務(wù)。值得一提的是,目前 EfficientNet 還沒有較為優(yōu)雅的 Julia 封裝,實(shí)屬一大遺憾。 using Metalhead vgg = VGG19() model = Chain(vgg.layers[1:end-6], Dense(512, 4096, relu), Dropout(0.5), Dense(4096, 4096, relu), Dropout(0.5), Dense(4096, 10)) |> device Flux.trainmode!(model, true)
為了方便試驗(yàn)和記錄,我們參照官方實(shí)現(xiàn)封裝超參數(shù)和訓(xùn)練過程。在訓(xùn)練過程中,我們可以定義一個回調(diào)函數(shù)打印驗(yàn)證集的損失函數(shù):throttle(() -> @ show (loss(val...)), args.throttle)。 using Parameters: @with_kw @with_kw mutable struct Args batchsize::Int = 128 throttle::Int = 10 lr::Float64 = 5e-5 epochs::Int = 10 splitr_::Float64 = 0.1 end function train(model; kws...) # Initialize the hyperparameters args = Args(; kws...) # Load the train, validation data train, val = get_processed_data(args) @info("Constructing Model") # Defining the loss and accuracy functions loss(x, y) = logitcrossentropy(model(x), y) ## Training # Defining the callback and the optimizer evalcb = throttle(() -> @show(loss(val...)), args.throttle) opt = ADAM(args.lr) @info("Training....") # Starting to train models [email?protected] args.epochs Flux.train!(loss, params(model), train, opt, cb=evalcb) end
3. ResNet + ImageNet 大型數(shù)據(jù)集上的標(biāo)準(zhǔn)訓(xùn)練過程
在學(xué)會在中小型數(shù)據(jù)集上完成試驗(yàn)后,我們往往要將試驗(yàn)遷移到大型數(shù)據(jù)集上。訓(xùn)練過程也會增加很多讀取、存儲、日志等內(nèi)容。由于篇幅限制,這里只說明重要部分,完整代碼請參考并運(yùn)行 ResNet+ImageNet 。
不同于 PyTorch,目前 Flux 對 Dataset 和 Dataloader 的支持十分有限。官方目前正著力于添加相關(guān)功能,不久后可能有相關(guān)實(shí)現(xiàn)。這里我們模仿 PyTorch 多線程讀取數(shù)據(jù)集并生成 Dataloader 。 struct ImagenetDataset # Data we're initialized with dataset_root::String batch_size::Int data_loader::Function # Data we calculate once, at startup filenames::Vector{String} queue_pool::QueuePool function ImagenetDataset(dataset_root::String, num_workers::Int, batch_size::Int, data_loader::Function = imagenet_val_data_loader) # Scan dataset_root for files filenames = filter(f -> endswith(f, ".JPEG"), recursive_readdir(dataset_root)) @assert !isempty(filenames) "Empty dataset folder!" @assert num_workers >= 1 "Must have nonnegative integer number of workers!" @assert batch_size >= 1 "Must have nonnegative integer batch size!" # Start our worker pool @info("Adding $(num_workers) new data workers...") queue_pool = QueuePool(num_workers, data_loader, quote # The workers need to be able to load images and preprocess them via Metalhead using Flux, Images, Metalhead include($(@__FILE__)) end) return new(dataset_root, batch_size, data_loader, filenames, queue_pool) end end # Serialize the arguments needed to recreate this ImagenetDataset function freeze_args(id::ImagenetDataset) return (id.dataset_root, length(id.queue_pool.workers), id.batch_size, id.data_loader) end Base.length(id::ImagenetDataset) = div(length(id.filenames),id.batch_size) mutable struct ImagenetIteratorState batch_idx::Int job_offset::Int function ImagenetIteratorState(id::ImagenetDataset) @info("Creating IIS with $(length(id.filenames)) images") # Build permutation for this iteration permutation = shuffle(1:length(id.filenames)) # Push first job, save value to get job_offset (we know that all jobs # within this iteration will be consequtive, so we only save the offset # of the first one, and can use that to determine the job ids of every # subsequent job: filename = joinpath(id.dataset_root, id.filenames[permutation[1]]) job_offset = push_job!(id.queue_pool, filename) # Next, push every other job for pidx in permutation[2:end] filename = joinpath(id.dataset_root, id.filenames[pidx]) push_job!(id.queue_pool, filename) end return new( 0, job_offset, ) end end function Base.iterate(id::ImagenetDataset, state=ImagenetIteratorState(id)) # If we're at the end of this epoch, give up the ghost if state.batch_idx > length(id) return nothing end # Otherwise, wait for the next batch worth of jobs to finish on our queue pool next_batch_job_ids = state.job_offset .+ (0:(id.batch_size-1)) .+ id.batch_size*state.batch_idx # Next, wait for the currently-being-worked-on batch to be done. pairs = fetch_result.(Ref(id.queue_pool), next_batch_job_ids) state.batch_idx += 1 # Collate X's and Y's into big tensors: X = cat((p[1] for p in pairs)...; dims=ndims(pairs[1][1])) Y = cat((p[2] for p in pairs)...; dims=ndims(pairs[1][2])) # Return the fruit of our labor return (X, Y), state end
Julia 使用 BSON 實(shí)現(xiàn)模型的持久化和讀取,速度令人滿意。對模型保存和讀取進(jìn)行封裝的相關(guān)實(shí)現(xiàn)如下: using BSON using Tracker using Statistics, Printf using Flux.Optimise function save_model(model, filename) model_state = Dict( :weights => Tracker.data.(params(model)) ) open(filename, "w") do io BSON.bson(io, model_state) end end function load_model!(model, filename) weights = BSON.load(filename)[:weights] Flux.loadparams!(model, weights) return model end
4. DCGAN+Fashion/GCN+Cora 其他網(wǎng)絡(luò)結(jié)構(gòu)與數(shù)據(jù)集
近年來 GAN 和 GCN 方興未艾,只實(shí)用 Julia 完成圖像分類任務(wù)還遠(yuǎn)遠(yuǎn)不夠。因此筆者正盡可能復(fù)現(xiàn)多種類的網(wǎng)絡(luò)結(jié)構(gòu)和任務(wù)。以 GAN 和 GCN 為例,Julia 已經(jīng)能很好地完成試驗(yàn)?zāi)繕?biāo)了。由于篇幅限制,這里只說明重要部分,完整代碼請參考并運(yùn)行 DCGAN+Fashion 和 GCN+Cora 。
與 CNN 相同,使用 Flux 可以輕松實(shí)現(xiàn)對 DCGAN 的定義。 function Discriminator() return Chain( Conv((4, 4), 1 => 64; stride = 2, pad = 1), x->leakyrelu.(x, 0.2f0), Dropout(0.25), Conv((4, 4), 64 => 128; stride = 2, pad = 1), x->leakyrelu.(x, 0.2f0), Dropout(0.25), x->reshape(x, 7 * 7 * 128, :), Dense(7 * 7 * 128, 1)) end function Generator(latent_dim) return Chain( Dense(latent_dim, 7 * 7 * 256), BatchNorm(7 * 7 * 256, relu), x->reshape(x, 7, 7, 256, :), ConvTranspose((5, 5), 256 => 128; stride = 1, pad = 2), BatchNorm(128, relu), ConvTranspose((4, 4), 128 => 64; stride = 2, pad = 1), BatchNorm(64, relu), ConvTranspose((4, 4), 64 => 1, tanh; stride = 2, pad = 1), ) end
遵循動態(tài)圖的反向更新策略,我們只需要像 PyTorch 一樣定義對抗損失和對抗訓(xùn)練過程,也較為簡單。 function discriminator_loss(real_output, fake_output) real_loss = mean(logitbinarycrossentropy.(real_output, 1f0)) fake_loss = mean(logitbinarycrossentropy.(fake_output, 0f0)) return real_loss + fake_loss end generator_loss(fake_output) = mean(logitbinarycrossentropy.(fake_output, 1f0)) function train_discriminator!(gen, dscr, x, opt_dscr, args) noise = randn!(similar(x, (args.latent_dim, args.batch_size))) fake_input = gen(noise) ps = Flux.params(dscr) # Taking gradient loss, back = Flux.pullback(ps) do discriminator_loss(dscr(x), dscr(fake_input)) end grad = back(1f0) update!(opt_dscr, ps, grad) return loss end function train_generator!(gen, dscr, x, opt_gen, args) noise = randn!(similar(x, (args.latent_dim, args.batch_size))) ps = Flux.params(gen) # Taking gradient loss, back = Flux.pullback(ps) do generator_loss(dscr(gen(noise))) end grad = back(1f0) update!(opt_gen, ps, grad) return loss end for ep in 1:args.epochs @info "Epoch $ep" for x in data loss_dscr = train_discriminator!(g_model, d_model, x, opt_dscr, args) loss_gen = train_generator!(g_model, d_model, x, opt_gen, args) end train_steps += 1 end
對于其他較為復(fù)雜的 CNN 模型,例如 UNet,用戶也可以自定義模塊的調(diào)用過程(類似于 PyTorch 中的 forward ): function UNet() conv_block = (block1(1, 32), block2(32, 32*2), block2(32*2, 32*4), block2(32*4, 32*8)) conv_block2 = (block1(32*16, 32*8), block1(32*8, 32*4), block1(32*4, 32*2), block1(32*2, 32)) bottle = block2(32*8, 32*16) upconv_block = (upconv(32*16, 32*8), upconv(32*8, 32*4), upconv(32*4, 32*2), upconv(32*2, 32)) conv_ = conv(32, 1) UNet(conv_block, conv_block2, bottle, upconv_block, conv_) end function (u::UNet)(x) enc1 = u.conv_block[1](x) enc2 = u.conv_block[2](enc1) enc3 = u.conv_block[3](enc2) enc4 = u.conv_block[4](enc3) bn = u.bottle(enc4) dec4 = u.upconv_block[1](bn) dec4 = cat(dims=3, dec4, enc4) dec4 = u.conv_block2[1](dec4) dec3 = u.upconv_block[2](dec4) dec3 = cat(dims=3, dec3, enc3) dec3 = u.conv_block2[2](dec3) dec2 = u.upconv_block[3](dec3) dec2 = cat(dims=3, dec2, enc2) dec2 = u.conv_block2[3](dec2) dec1 = u.upconv_block[4](dec2) dec1 = cat(dims=3, dec1, enc1) dec1 = u.conv_block2[4](dec1) dec1 = u.conv_(dec1) end model = UNet()
在 GNN 模型方面,目前較為流行的 GNN 庫是 GeometricFlux,但是由于剛剛開源不久,數(shù)據(jù)讀取方面的支持有限。實(shí)現(xiàn)應(yīng)當(dāng)是參考了 DGL,較為優(yōu)雅且易于擴(kuò)展。筆者目前也正在試圖基于 LightGraphs 開發(fā)一個 GNN 庫,主要著力于圖的構(gòu)建和分布式訓(xùn)練部分。 using GeometricFlux model = Chain(GCNConv(adj_mat, num_features=>hidden, relu), Dropout(0.5), GCNConv(adj_mat, hidden=>target_catg), softmax) |> gpu
三、后記
上述示例代碼和講解均來源于筆者的開源項(xiàng)目 Julia-Deeplearning,目前已有的最佳實(shí)踐包括: Julia 教程 基礎(chǔ)語法 卷積神經(jīng)網(wǎng)絡(luò) MLP+MNIST VGG+Cifar10 ResNet+ImageNet UNet+ISBI 生成對抗網(wǎng)絡(luò) DCGAN+Fashion 圖卷積網(wǎng)絡(luò) GCN+Cora
由于筆者近期試驗(yàn)較多,因此只能在試驗(yàn)之余偶爾更新。如果同學(xué)們有相關(guān)工作歡迎 PR 和提 Issue,衷心希望能夠拋磚引玉對大家有所幫助~
我有一個 anti-spam 模型,但是我用的是離線數(shù)據(jù),我在想能不能使用真實(shí)生產(chǎn)環(huán)境下的數(shù)據(jù)去訓(xùn)練模型呢?如果可以的話,求大家指教一下流程
目標(biāo)檢測是計(jì)算機(jī)視覺領(lǐng)域的傳統(tǒng)任務(wù),需要識別出圖像上存在的物體,給出對應(yīng)的類別,并將該物體的位置通過最小包圍框( Bounding box )的方式給出。下面介紹 2D 目標(biāo)檢測任務(wù)的常用評價指標(biāo)。 IoU ( Intersection over Union )
定義:交并比,兩個矩形框交集的面積與并集的面積之比 范圍:0~100% 用途:判斷兩個矩形框的重疊程度,值越高則重疊程度越高,即兩個框越靠近 IoU 和 Overlap Rate 定義是完全相同的,只不過在檢測任務(wù)中常寫作 IoU,在跟蹤任務(wù)中常寫作 Overlap Rate 附:3D 目標(biāo)檢測中的 IoU
IoU threshold 定義:需要人為設(shè)定的 IoU 的閾值,高于該閾值的矩形框被認(rèn)為是命中目標(biāo),反之則被認(rèn)為未命中 范圍:0~100% 用途:作為區(qū)分矩形框是否命中目標(biāo)的指標(biāo),閾值越高則要求矩形框與真值重疊程度越高 附:3D 目標(biāo)檢測中的 IoU threshold
TP,TN,FP,FN 正樣本 負(fù)樣本 判斷為正 TP FP 判斷為負(fù) FN TN TP ( True Positive,真陽性):檢測器給出正樣本,事實(shí)上也是正樣本,即正確檢測到目標(biāo) TN ( True Negative,真陰性):檢測器給出負(fù)樣本,事實(shí)上也是負(fù)樣本,即正確檢測到非目標(biāo) FP ( False Positive,假陽性):檢測器給出正樣本,事實(shí)上卻是負(fù)樣本,即誤檢測 FN ( False Negative,假陰性):檢測器給出負(fù)樣本,事實(shí)上卻是正樣本,即漏檢測 附:3D 目標(biāo)檢測中的 TP,T N,FP,FN
Precision 定義:精確度(查準(zhǔn)率),表示檢測器給出所有正樣本中正確的百分比 計(jì)算:Precision = 檢測器正確檢測的正樣本個數(shù) /檢測器給出的所有正樣本個數(shù) = TP/(TP + FP) 范圍:0~100% 用途:用于評估檢測器在檢測成功基礎(chǔ)上的正確率 附:3D 目標(biāo)檢測中的 Precision
Recall 定義:召回率(查全率),表示真值給出的所有正樣本中被檢測器檢測到的百分比 計(jì)算:Recall = 檢測器正確檢測的正樣本個數(shù) /真值給出的所有正樣本個數(shù) = TP/(TP + FN) 范圍:0~100% 用途:用于評估檢測器對所有待檢測目標(biāo)的檢測覆蓋率 附:3D 目標(biāo)檢測中的 Recall
PR 曲線
定義:Precision-Recall 曲線,即以 Precision 為縱坐標(biāo)、Recall 為橫坐標(biāo)所作的曲線 繪制:選取不同的 confidence 閾值,可以在 PR 坐標(biāo)系上得到不同的點(diǎn),連接這些點(diǎn)即可獲得 PR 曲線 用途:用來評估模型性能。Precision 值和 Recall 值越大越好,所以 PR 曲線越往右上角凸越好。 附:3D 目標(biāo)檢測中的 PR 曲線
AP ( Average Precision ) 定義:平均精度,即 PR 曲線下的面積 范圍:0~100% 用途:用來衡量算法在單個類別上的平均精度。AP 值越高,表示對這個類別的檢測精度越高。 計(jì)算:11 點(diǎn)插值法和所有點(diǎn)插值法 11 點(diǎn)插值法:VOC2010 以前,選取當(dāng) Recall >= 0, 0.1, 0.2, ..., 1 共 11 個點(diǎn)時的 Precision 最大值,AP 是這 11 個 Precision 的平均值,此時只由 11 個點(diǎn)去近似 PR 曲線下面積。
所有點(diǎn)插入法:從 2010 年開始,PASCAL VOC 計(jì)算 AP 的方法發(fā)生了變化,使用所有點(diǎn)插入法而不是 11 個等間隔點(diǎn)插入了。
在這種情況下,不再使用僅在幾個點(diǎn)上觀察到的 precision,而是通過對每個 precision 進(jìn)行插值來獲得 AP 。這樣,我們可以估計(jì)曲線下的面積。 附:3D 目標(biāo)檢測中的 AP
mAP ( Mean Average Precision ) 定義:平均精度均值,即多個類別的 AP 的平均值 范圍:0~100% 用途:用來衡量算法在所有類別上的整體精度表現(xiàn)。mAP 值是目標(biāo)檢測算法最重要的評估指標(biāo)之一。 附:3D 目標(biāo)檢測中的 mAP
f-measure 定義:Precision 和 Recall 的加權(quán)調(diào)和平均值 計(jì)算: fβ-measure = ( β^2 + 1 ) * P * R / ( β^2 * P + R ) 當(dāng) β = 1 時,即為 f1-measure: f1-measure = 2 * P * R / ( P + R ) 范圍:0~100% 用途:Precision 和 Recall 在一般情況下是互斥的,即 Precision 越高,Recall 越低。f-measure 用于為兩者給出一個綜合評判。 附:3D 目標(biāo)檢測中的 f-measure
FPPI ( False Positive Per Image ) 定義:每張圖片的誤檢數(shù),相比 precision 和 recall 更加直觀 用途:評估平均誤檢數(shù)
卑微菜雞在這里懇求各位圖像視覺的大佬指點(diǎn)一下我,如何解決下面的 bug(有償也可!歡迎留下聯(lián)系方式)
問題描述:畢設(shè)要做的是用 fcn 做顯著性目標(biāo)識別,大概過程就是用 fcn 做多分類的語義分割,然而結(jié)果輸出卻成了雪花狀的圖,雖然能看出大概的輪廓。訓(xùn)練過程中 loss 一直在抖動,范圍在 0.4 到幾之間,不收斂。
自我感覺是在數(shù)據(jù)處理方面出了問題。 實(shí)驗(yàn)室的學(xué)姐也沒有解決的思路,網(wǎng)上也查不到。迫不得已來 v2 懇求各位大佬。(有償也可啊!)
我的微信:sy971216200
昨天發(fā)了一個旅行家問題的算法貼,V 友一致認(rèn)為不能算算法貼,本身屬于 NP 困難問題沒什么確切算法可言。
最后想了想,因?yàn)楸举|(zhì)需要完成的是分類任務(wù),既然無法求得理論上最完美的分類解,那只能用其他辦法嘗試逼近了,目前想到比較簡單的做法是把高維特征向量用 TSNE 降維,然后跑 kmeans 分類。
對機(jī)器學(xué)習(xí)算法大部分不求甚解,于是做了調(diào)包俠,用 sklearn 里寫好的 tsne 跑。但是剛才跑了幾個測試數(shù)據(jù)感覺都不太對,有沒有機(jī)器學(xué)習(xí)算法大佬來指點(diǎn)一下
====================================================一個簡單的示例代碼 from sklearn.manifold import TSNE import numpy as np X = [ [10, 56, 12 , 10, 56, 12], [10, 56, 12 , 10, 56, 12], [80, 21, 92 , 80, 21, 92], [21, 30, 53 , 21, 30, 53], [11, 81, 15 , 11, 81, 15], [11, 81, 15 , 11, 81, 14] ] model = TSNE(n_components=3 ,learning_rate = 1000 , n_iter = 1000 , init = 'pca') Y = model.fit_transform(X)
====================================================得到結(jié)果 [[-199.75327 -165.53574 -241.92471 ] [ 171.09433 -41.500977 117.659996] [ 362.16272 397.58792 34.34646 ] [-256.5073 -221.04861 304.41156 ] [ -26.240019 322.07495 -296.1156 ] [-339.0215 261.51828 109.22412 ]]
====================================================
如題,我嘗試將模擬的六維數(shù)據(jù)降到三維,數(shù)據(jù)完全是隨機(jī)生成的,并沒有什么實(shí)際意義,但是數(shù)據(jù)中一個關(guān)鍵點(diǎn)是,第一和第二組數(shù)據(jù)完全一致,倒數(shù)第一和倒數(shù)第二組數(shù)據(jù)只有極微弱差距。
所以按照理論上,我希望得到的降維數(shù)據(jù)中,第一和第二個結(jié)果應(yīng)該是完全一致,或者說是非常相似的。 但是跑了幾次得到的結(jié)果都是相差非常遠(yuǎn)。想問問帶佬們這是為什么,這個結(jié)果是正常的嗎還是我寫錯了。
小白一枚哈,剛接觸知識圖譜。看了一下教程大部分是把關(guān)系型數(shù)據(jù)庫的數(shù)據(jù)導(dǎo)入到圖數(shù)據(jù)庫,想問下導(dǎo)入之后,節(jié)點(diǎn)我能理解,但是關(guān)系是怎么產(chǎn)生的呢? 我導(dǎo)入很多很多節(jié)點(diǎn),我自己都不知道他們確切的關(guān)系,如何能在圖數(shù)據(jù)庫中自動生成關(guān)系呢?可以自定義規(guī)則嗎?這個流程是什么呀。。。大佬們幫幫忙
想請教大家一個問題, 我在使用 cnn 卷積神經(jīng)網(wǎng)絡(luò)分類數(shù)字 0-10, 訓(xùn)練完后測試效果還不錯, 但有個問題, 當(dāng)我傳入一張沒有內(nèi)容的圖片, 又或者傳入一張不含數(shù)字內(nèi)容的圖片, 這時網(wǎng)絡(luò)還會把它們歸類到數(shù)字, 這就讓我很頭疼了, 于是我在網(wǎng)上找了個 ocr 進(jìn)行測試, 發(fā)現(xiàn)他們的 ocr 能知道我傳入的圖片中沒有內(nèi)容, 即使圖片中亂涂亂畫也能識別出沒有內(nèi)容, 請問這是怎么做到的, 請幫幫我!
一個基于 Postgres 的時間序列( time-series )數(shù)據(jù)庫:https://www.timescale.com/ 這是一個用各種時間維度查詢紐約市出租車出行數(shù)據(jù)的例子: https://docs.timescale.com/v0.9/tutorials/tutorial-hello-nyc 如果你的數(shù)據(jù)類型就是各種數(shù)值 + 時間(比如各種計(jì)費(fèi)數(shù)據(jù)或是來自傳感器的數(shù)據(jù)),那么 Timescale 會是一個很值得嘗試的方案。
cloudflare 的 Pro 里面的一些功能規(guī)則正在學(xué)習(xí),有些配置下來發(fā)現(xiàn)不是很理想,主要達(dá)不到那個效果。想知道有沒有大佬愿意分享這方面的信息給學(xué)習(xí)下。
當(dāng)然不會白拿大佬的成果,愿以 Pro 功能計(jì)劃作為回報。當(dāng)然微不足道,但也是誠意。希望能遇到不吝賜教的大佬。
感謝
那如果我用 digitalocean 套 CF 是不是 就不計(jì)算流量的,因?yàn)槲乙鲆粢曨l,如果這樣是不是就可以省錢了,有了解的小伙伴嗎?謝謝。
要使用 Cloudflare 的中國節(jié)點(diǎn)門檻很高,要企業(yè)版并在大陸有備案才能使用到相關(guān)節(jié)點(diǎn),其他的都只是全球 IP 池自動適配。但是 Pro 跟 Business 的區(qū)別在價錢上差很多,但在功能上雖有差別,但也能能過一些方法解決升級它的功能。這就感覺 Business 計(jì)劃這價錢有點(diǎn)尷尬。我是這么認(rèn)為的。 v2ex 也用了 CF 的 CDN,不知道是企業(yè)版還是什么定制版本。
cf 好像可以簽發(fā)任意域名的證書,然后如果在 ISP 里劫持了某個域名的解析結(jié)果,再配合這個簽發(fā)的證書,是不是就可以中間人了。
https://support.cloudflare.com/hc/en-us/articles/115001992652-Using-Privacy-Pass-with-Cloudflare https://privacypass.github.io/ Cloudflare 推薦的一個的瀏覽器插件,或許可以在某些情況下為你節(jié)約一些時間。支持 Chrome 和 Firefox,安裝之后,你可以主動用它做 CAPTCHA 驗(yàn)證,成功之后獲得點(diǎn)數(shù)。如果再遇到其他需要 CAPTCHA 的網(wǎng)站,就可以消耗點(diǎn)數(shù),而不需要重新做 CAPTCHA 。
網(wǎng)站整天被各種機(jī)器人掃描和采集,實(shí)在是太煩了 cloudflare 的 bot management 和 anti-bot,看介紹似乎可以 https://www.cloudflare.com/products/bot-management/ https://blog.cloudflare.com/cleaning-up-bad-bots/
不知道有沒有人用過?效果如何
另外,似乎 cloudflare 國內(nèi)節(jié)點(diǎn)是和百度云加速合作的,那么,效果是一樣的么?
加入我們只需要和我們一起學(xué)習(xí)! 得到的確實(shí)扎實(shí)的理論計(jì)算機(jī)數(shù)學(xué)基礎(chǔ)! 介紹: https://www.yuque.com/abser/mathincs/preview 例子: https://www.yuque.com/abser/mathincs/theorem1.5.1
最近接手一個圖形項(xiàng)目,比較多的算法,昨天一個對數(shù)公式都想不起來,今天突然想起才解決了一個 BUG,可惡的是我之前花了 1 天時間去研究相關(guān)的東西而無果。。。
現(xiàn)在呢,就是深感自己的數(shù)學(xué)知識薄弱,希望想再補(bǔ)一補(bǔ),和圖形、AI 之類的知識我都想學(xué)。
各位有沒有什么書比較推薦的,實(shí)在沒有,我可能要買一本公式大全了,,,哈哈!求推薦求介紹。
用謂詞邏輯符號化以下命題,并推證結(jié)論的有效性。 “任何人如果他喜歡美術(shù),他就不喜歡體育。每個人或喜歡體育,或喜歡音樂,有的人 不喜歡音樂,因而有的人不喜歡美術(shù)?!?設(shè) R 是集合 A 上的二元關(guān)系,如果對于任意的 a, b, c∈A,若 aRb 且 bRc,則有 cRa,那 么稱 R 是循環(huán)的。 證明:R 是等價關(guān)系當(dāng)且僅當(dāng) R 是自反的和循環(huán)的。
在數(shù)學(xué)模式中,我們希望通過按鍵序列 1/獲得一個以 1 為分子的分式,并把光標(biāo)移動到分母中,以方便后續(xù)輸入。
這樣,不斷按 1/1+1/1+1/1+1/1+1/ ... 就能得到如上數(shù)學(xué)式子。
可以通過如下方式實(shí)現(xiàn)這樣的功能: 點(diǎn)擊工具→開發(fā)工具,激活開發(fā)者菜單 點(diǎn)擊開發(fā)者→Open my-init-texmacs.scm 在該文件的末尾添加如下代碼 重啟 TeXmacs (kbd-map (:mode in-math?) ("1 /" (begin (math-insert `(frac "1" "")) (go-to-previous-word)))))
然后,你就可以愉快地在數(shù)學(xué)模式中,用 1/快速輸入分子為 1 的分式了。
原文鏈接: http://tmml.wiki/2019/09/13/texmacs-kbd-frac/
https://www.bilibili.com/video/av71848818?share_medium=android&share_source=more&bbid=XY7209B7792B4FD102B98029D9F07DF81BD80&ts=1571551662834
假設(shè)企業(yè)有四個項(xiàng)目,分?jǐn)?shù)分別為[100,90,100,80],經(jīng)過分?jǐn)?shù)排序后得到[100,100,90,80],然后公式為(1 100+2 100+3 90+4 80)/(1+2+3+4)=89.00
請問各位 v 友,在注冊數(shù)緣社區(qū)時出現(xiàn)數(shù)據(jù)格式錯誤,無法解析是什么情況?該怎么解決?
大學(xué)的時候數(shù)學(xué)類沒學(xué)好,現(xiàn)在想玩玩 AI 方向,連門都進(jìn)不去。B 站的那個“線性代數(shù)的本質(zhì)”我已經(jīng)看過了,但是我感覺這教程可能不太適合我,我看到一半的時候,疑問反而變多了,尤其是有一種強(qiáng)烈的“雖然你說線性代數(shù)就是這樣的,但是為什么線性代數(shù)是這樣的呢”這種奇怪的疑問
這個 PDF 列出了各種 LaTeX 輸入數(shù)學(xué)符號、字母符號及其他特殊符號的代碼,還有一些特殊格式代碼,方便各位 V 友查閱。注意使用時須用 amssymb 這個宏包。
http://www.caam.rice.edu/~heinken/latex/symbols.pdf
我是一名程序員,想補(bǔ)一下數(shù)學(xué)知識 大家有什么好的學(xué)習(xí)資源或者課程 書籍推薦么 比如線代,離散數(shù)學(xué),具體數(shù)學(xué),有什么好的方法提升編程邏輯思維能力
0.9 ,9 的循環(huán),究竟是不是等于 1 來著,直觀看來是肯定不等于的,但是從極限的角度來說,肯定是等于的~
首先按照https://zhuanlan.zhihu.com/p/56942597 編譯安裝好 GNU Guile 1.8.8。 然后安裝一下構(gòu)建 GNU TeXmacs 所需的依賴。 最后: git clone [email?protected] :texmacs/texmacs.gitcd texmacs cmake/ build_deb.sh 在執(zhí)行 build_deb.sh 這個腳本的時候,可能會遇到各種編譯需要的依賴缺失,這個時候用 apt 裝一下就可以了。最后編譯完成后,使用 dpkg -i 安裝相關(guān)的 deb 即可: sudo dpkg -i ../texmacs_1.99.9-1_amd64.deb 有啥問題可以在評論區(qū)直接問我。 原文鏈接: https://zhuanlan.zhihu.com/p/60232945