一、簡介
faked 是一個在前端開發中用于 mock 服務端接口的模塊,輕量簡單,無需要在本地啟動 Server 也無需其它更多的資源,僅在瀏覽器中完成「請求拉截」,配合完整的「路由系統」輕而易舉的 mock 后端 API。
GitHub Rep 地址: https://github.com/Houfeng/faked
二、安裝 faked
有兩種可選安裝方式,你可以通過傳統的 sciprt
方式引入 faked,如果你采用了 CommonJs 或 ES6 Modules 模塊方案,也可通過安裝 NPM Pageage 的方式安裝依賴。
通過 script 引入:
<script src="your-path/faked.min.js"></script>
通過 npm 安裝:
$ npm i faked --save-dev
CommonJs 方式引用
const faked=require('faked');
ES6 Modules 方式引用
import faked from 'faked'
三、基本使用
通過 faked.when
方法你幾乎就可以使用 faked 的所有功能了,盡管 faked 還提供了一組「快捷方法」, faked.when
方法說明如下:
//指定單一 Http Method
示例,模擬一個獲取用戶信息的接口,參考如下代碼:
faked.when('get','/user/{id}', function(){
每一個 handler
的 this
就是當前請求上下文對象,對象有如下主要成員:
this.send(data, status, headers)
方法,用于響應一個請求,status 默認為 200
this.params
路由參數對象,用于訪問路由模式中的「路由參數」,如上邊示例中的 id
this.query
解析查詢字符串對應的對象,比如 ?name=bob
可以通過 this.query.name
訪問
this.body
請求的主體內容,通常會是一個 json
對象,它取決于發起的請求。
除了使用 send
方法,還可以直接「返回」數據,參考如下代碼:
faked.when('get','/user/{id}', function(){ return {name:'Bob'};
當然,在有「異步處理」時你也可以返回一個 promise
對象或像上邊那樣用 send
方法。如果你只想 mock 數據,還可以使用簡化寫法,參考如下代碼:
faked.when('get','/user/{id}', {name:'bob'});
四、快捷方法
faked 還基于 when 方法提供了一組快捷方法,對應常用的 Http Methods,包括:
get, post, put, delete, options, patch, head, copy, link, unlink, lock, unlock, view
用 faked.get
寫一個示例:
faked.get('/user/{id}',function(){ this.send({name:'Bob'});
其它快捷方法和 faked.get
用法完全一致。
五、路由系統
在編輯 Mock API 時, faked 提供了路由支持,如上邊看到的 /user/{id}
,就是一個路由「匹配模式」,其中 {id}
是一個路由參數,當多個路由同時匹配請求的 URL 時,只會觸發第一個執行,不同的 Http Method
的 URL 匹配模式可以相同,并不會沖突。路由參數還可以加「限定表達式」,參考如下代碼:
// User Id 只能是數字faked.get('/user/{id:\d+}', {name:'test'});
六、模擬網絡延時
有時候,我們希望 Mock API 能延時響應數據,以模擬「網絡延時」,faked 目前支持固定的「延時設置」,參考如下代碼:
const faked=require('faked');//所有的請求都將被延時 2 秒種再響應用 mock 數據faked.delay=2000;
當 delay 設置 0 時,將禁用延時。
七、設置超時時間
faked 還可設置 Mock API 的最大響應時間,這項設置存在的意義還在于「所有 Mock API 的 Handler 默認都是異步的,如果忘記「返回或 Send」一個響應結果,請求將會被一直掛起,有了超時設置,超時時將會拋出一個錯誤,方便定位問題」,參考如下代碼:
const faked=require('faked');//在超過 8 秒未響應數據時,將會打印一個錯誤消息faked.timeout=8000;
超時設置和延時設置并不會相互影響,超時計算是從延時結束后開始的。
八、JSONP 處理
faked 除了能 mock 常規的 ajax
和 fetch
請求,還能 mock 常常用來處理跨域問題的 jsonp
請求,faked 有兩個參數用于配置 jsonp,參考如下代碼:
//指定服務端用于獲取「回調函數名」的 「QueryString 參數」
九、注意事項
faked 是一個「輔助開發」的工具,除非有特殊需要,一般情況下它不應出現在你的生產代碼中,所以需要注意:
請勿將 faked 放到「生產環境」的應用或頁面中
找一個合適的你項目的方式決定什么時引用 faked
比如,在 webpak
中,可以根據環境變量決定入口文件,并只在 mock
的入口文件中引用 faked,示例:
webpack.config.js
module.exports={
然后,在 index.mock.js
中這樣寫:
require('./mock')require('./index');
用于存放的 mock
代碼的 mock.js
可能是這樣的:
const faked=require('faked');
當然,你可以根據實際情況,安排你的文或目錄結構。
-- END --
選自Paperspace Blog
作者:Felipe
機器之心編譯
參與:Jane W、黃小天
「大多數人類和動物學習是無監督學習。如果智能是一塊蛋糕,無監督學習是蛋糕的坯子,有監督學習是蛋糕上的糖衣,而強化學習則是蛋糕上的櫻桃。我們知道如何做糖衣和櫻桃,但我們不知道如何做蛋糕。」
Facebook 人工智能研究部門負責人 Yann LeCun 教授在講話中多次提及這一類比。對于無監督學習,他引用了「機器對環境進行建模、預測可能的未來、并通過觀察和行動來了解世界如何運作的能力」。
深度生成模型(deep generative model)是嘗試解決機器學習中無監督學習問題的技術之一。在此框架下,需要一個機器學習系統來發現未標記數據中的隱藏結構。深度生成模型在許多應用中有許多廣泛的應用,如密度估計、圖像/音頻去噪、壓縮、場景理解(scene understanding)、表征學習(representation learning)和半監督分類(semi-supervised classification)。
變分自編碼器(Variational Autoencoder/VAE)使得我們可以在概率圖形模型(probabilistic graphical model)的框架下將這個問題形式化,在此框架下我們可以最大化數據的對數似然值的下界。在本文中,我們將介紹一種最新開發的架構,即對抗自編碼器(Adversarial Autoencoder),它由 VAE 啟發,但它在數據到潛在維度的映射方式中(如果現在還不清楚,不要擔心,我們將在本文中重新提到這個想法)有更大的靈活性。關于對抗自編碼器最有趣的想法之一是如何通過使用對抗學習(adversarial learning)將先驗分布(prior distribution)運用到神經網絡的輸出中。
如果想將深入了解 Pytorch 代碼,請訪問 GitHub repo(https://github.com/fducau/AAE_pytorch)。在本系列中,我們將首先介紹降噪自編碼器和變分自編碼器的一些背景,然后轉到對抗自編碼器,之后是 Pytorch 實現和訓練過程以及 MNIST 數據集使用過程中一些關于消糾纏(disentanglement)和半監督學習的實驗。
背景
降噪自編碼器(DAE)
我們可在自編碼器(autoencoder)的最簡版本之中訓練一個網絡以重建其輸入。換句話說,我們希望網絡以某種方式學習恒等函數(identity function)f(x)=x。為了簡化這個問題,我們將此條件通過一個中間層(潛在空間)施加于網絡,這個中間層的維度遠低于輸入的維度。有了這個瓶頸條件,網絡必須壓縮輸入信息。因此,網絡分為兩部分:「編碼器」用于接收輸入并創建一個「潛在」或「隱藏」的表征(representation);「解碼器」使用這個中間表征,并重建輸入。自編碼器的損失函數稱為「重建損失函數(reconstruction loss)」,它可以簡單地定義為輸入和生成樣本之間的平方誤差:
當輸入標準化為在 [0,1] N 范圍內時,另一種廣泛使用的重建損失函數是交叉熵(cross-entropy loss)。
變分自編碼器(VAE)
變分自編碼器對如何構造隱藏表征施加了第二個約束。現在,潛在代碼的先驗分布由設計好的某概率函數 p(x)定義。換句話說,編碼器不能自由地使用整個潛在空間,而是必須限制產生的隱藏代碼,使其可能服從先驗分布 p(x)。例如,如果潛在代碼上的先驗分布是具有平均值 0 和標準差 1 的高斯分布,則生成值為 1000 的潛在代碼應該是不可能的。
這可以被看作是可以存儲在潛在代碼中的信息量的第二類正則化。這樣做的好處是現在我們可以作為一個生成模型使用該系統。為了創建一個服從數據分布 p(x)的新樣本,我們只需要從 p(z)進行采樣,并通過解碼器來運行該樣本以重建一個新圖像。如果不施加這種條件,則潛在代碼在潛在空間中的分布是隨意的,因此不可能采樣出有效的潛在代碼來直接產生輸出。
為了強制執行此屬性,將第二項以先驗分布與編碼器建立分布之間的 KL 散度(Kullback-Liebler divergence)的形式添加到損失函數中。由于 VAE 基于概率解釋,所使用的重建損失函數是前面提到的交叉熵損失函數。把它們放在一起我們有:
或
其中 q(z|x) 是我們網絡的編碼器,p(z) 是施加在潛在代碼上的先驗分布。現在這個架構可以使用反向傳播(backpropagation)聯合訓練。
對抗自編碼器(AAE)
作為生成模型的對抗自編碼器
變分自編碼器的主要缺點之一是,除了少數分布之外,KL 散度項的積分不具有封閉形式的分析解法。此外,對于潛在代碼 z 使用離散分布并不直接。這是因為通過離散變量的反向傳播通常是不可能的,使得模型難以有效地訓練。這篇論文介紹了在 VAE 環境中執行此操作的一種方法(https://arxiv.org/abs/1609.02200)。
對抗自編碼器通過使用對抗學習(adversarial learning)避免了使用 KL 散度。在該架構中,訓練一個新網絡來有區分地預測樣本是來自自編碼器的隱藏代碼還是來自用戶確定的先驗分布 p(z)。編碼器的損失函數現在由重建損失函數與判別器網絡(discriminator network)的損失函數組成。
圖中顯示了當我們在潛在代碼中使用高斯先驗(盡管該方法是通用的并且可以使用任何分布)時 AAE 的工作原理。最上面一行相當于 VAE。首先,根據生成網絡 q(z|x) 抽取樣本 z,然后將該樣本發送到根據 z 產生 x' 的解碼器。在 x 和 x' 之間計算重建損失函數,并且相應地通過 p 和 q 反向推導梯度,并更新其權重。
圖 1. AAE 的基本架構最上面一行是自編碼器,而最下面一行是對抗網絡,迫使到編碼器的輸出服從分布 p(z)。
在對抗正則化部分,判別器收到來自分布為 q(z|x)的 z 和來自真實先驗 p(z) 的 z' 采樣,并為每個來自 p(z)的樣本附加概率。發生的損失函數通過判別器反向傳播,以更新其權重。然后重復該過程,同時生成器更新其參數。
我們現在可以使用對抗網絡(它是自編碼器的編碼器)的生成器產生的損失函數而不是 KL 散度,以便學習如何根據分布 p(z)生成樣本。這種修改使我們能夠使用更廣泛的分布作為潛在代碼的先驗。
判別器的損失函數是
其中 m 是微批尺寸(minibatch size),z 由編碼器生成,z' 是來自真實先驗的樣本。
對于對抗生成器,我們有
通過查看方程式和曲線,你應該明白,以這種方式定義的損失函數將強制判別器能夠識別假樣本,同時推動生成器欺騙判別器。
定義網絡
在進入這個模型的訓練過程之前,我們來看一下如何在 Pytorch 中實現我們現在所做的工作。對于編碼器、解碼器和判別器網絡,我們將使用 3 個帶有 ReLU 非線性函數與概率為 0.2 的 dropout 的 1000 隱藏狀態層的簡單前饋神經網絡(feed forward neural network)。
在進入這個模型的訓練過程之前,我們來看一下如何在 Pytorch 中實現我們現在所做的工作。對于編碼器、解碼器和判別器網絡,我們將使用 3 個帶有 ReLU 非線性函數與概率為 0.2 的 dropout 的 1000 隱藏狀態層的簡單前饋神經網絡(feed forward neural network)。
#Encoderclass Q_net(nn.Module):def __init__(self): super(Q_net, self).__init__() self.lin1=nn.Linear(X_dim, N) self.lin2=nn.Linear(N, N) self.lin3gauss=nn.Linear(N, z_dim) def forward(self, x): x=F.droppout(self.lin1(x), p=0.25, training=self.training) x=F.relu(x) x=F.droppout(self.lin2(x), p=0.25, training=self.training) x=F.relu(x) xgauss=self.lin3gauss(x) return xgauss
# Decoderclass P_net(nn.Module):def __init__(self): super(P_net, self).__init__() self.lin1=nn.Linear(z_dim, N) self.lin2=nn.Linear(N, N) self.lin3=nn.Linear(N, X_dim) def forward(self, x): x=self.lin1(x) x=F.dropout(x, p=0.25, training=self.training) x=F.relu(x) x=self.lin2(x) x=F.dropout(x, p=0.25, training=self.training) x=self.lin3(x) return F.sigmoid(x)
# Discriminatorclass D_net_gauss(nn.Module):def __init__(self): super(D_net_gauss, self).__init__() self.lin1=nn.Linear(z_dim, N) self.lin2=nn.Linear(N, N) self.lin3=nn.Linear(N, 1) def forward(self, x): x=F.dropout(self.lin1(x), p=0.2, training=self.training) x=F.relu(x) x=F.dropout(self.lin2(x), p=0.2, training=self.training) x=F.relu(x) return F.sigmoid(self.lin3(x))
從這個定義可以注意到一些事情。首先,由于編碼器的輸出必須服從高斯分布,我們在最后一層不使用任何非線性定義。解碼器的輸出具有 S 形非線性,這是因為我們使用以其值在 0 和 1 范圍內的標準化輸入。判別器網絡的輸出僅為 0 和 1 之間的一個數字,表示來自真正先驗分布的輸入概率。
一旦網絡的類(class)定義完成,我們創建每個類的實例并定義要使用的優化器。為了在編碼器(這也是對抗網絡的生成器)的優化過程中具有獨立性,我們為網絡的這一部分定義了兩個優化器,如下所示:
torch.manual_seed(10) Q, P=Q_net()=Q_net(), P_net(0) # Encoder/Decoder D_gauss=D_net_gauss() # Discriminator adversarial if torch.cuda.is_available(): Q=Q.cuda() P=P.cuda() D_cat=D_gauss.cuda() D_gauss=D_net_gauss().cuda()# Set learning ratesgen_lr, reg_lr=0.0006, 0.0008# Set optimizatorsP_decoder=optim.Adam(P.parameters(), lr=gen_lr) Q_encoder=optim.Adam(Q.parameters(), lr=gen_lr) Q_generator=optim.Adam(Q.parameters(), lr=reg_lr) D_gauss_solver=optim.Adam(D_gauss.parameters(), lr=reg_lr)
訓練步驟
每個微批處理的架構的訓練步驟如下:
1)通過編碼器/解碼器部分進行前向路徑(forward path)計算,計算重建損失并更新編碼器 Q 和解碼器 P 網絡的參數。
z_sample=Q(X) X_sample=P(z_sample) recon_loss=F.binary_cross_entropy(X_sample + TINY, X.resize(train_batch_size, X_dim) + TINY) recon_loss.backward() P_decoder.step() Q_encoder.step()
2)創建潛在表征 z=Q(x),并從先驗函數的 p(z) 取樣本 z',通過判別器運行每個樣本,并計算分配給每個 (D(z) 和 D(z')) 的分數。
Q.eval() z_real_gauss=Variable(torch.randn(train_batch_size, z_dim) * 5) # Sample from N(0,5)if torch.cuda.is_available(): z_real_gauss=z_real_gauss.cuda() z_fake_gauss=Q(X)
3)計算判別器的損失函數,并通過判別器網絡反向傳播更新其權重。在代碼中,
# Compute discriminator outputs and loss D_real_gauss, D_fake_gauss=D_gauss(z_real_gauss), D_gauss(z_fake_gauss) D_loss_gauss=-torch.mean(torch.log(D_real_gauss + TINY) + torch.log(1 - D_fake_gauss + TINY)) D_loss.backward() # Backpropagate loss D_gauss_solver.step() # Apply optimization step
4)計算生成網絡的損失函數并相應地更新 Q 網絡。
# GeneratorQ.train() # Back to use dropout z_fake_gauss=Q(X) D_fake_gauss=D_gauss(z_fake_gauss)G_loss=-torch.mean(torch.log(D_fake_gauss + TINY)) G_loss.backward() Q_generator.step()
生成圖像
現在我們嘗試可視化 AAE 是如何將圖像編碼成具有標準偏差為 5 的 2 維高斯潛在表征的。為此,我們首先用 2 維隱藏狀態訓練模型。然后,我們在(-10,-10)(左上角)到(10,10)(右下角)的潛在空間上產生均勻點,并將其在解碼器網絡上運行。
潛在空間。同時在 x 和 y 軸上從 -10 到 10 均勻地探索 2 維潛在空間時的圖像重建。
AAE 學習消糾纏表征(disentangled representation)
數據的理想的中間表征將能夠捕獲產生觀測數據變異的潛在因素。Yoshua Bengio 及其同事在一篇論文中(http://www.cl.uni-heidelberg.de/courses/ws14/deepl/BengioETAL12.pdf)中注明:「我們希望我們的表征能夠消糾纏(解釋)變異因素。在輸入分布中,不同的數據解釋因素傾向于彼此獨立地變化」。他們還提到「最魯棒的特征學習方法是盡可能多地解釋因素,盡可能少地丟棄關于數據的信息」。
在 MNIST(http://yann.lecun.com/exdb/mnist/)數據(這是關于手寫數字的大數據集)下,我們可以定義兩個潛在的因果性因素,一方面是生成的數字,另一方面是書寫的風格或方式。
監督式方法
在這部分中,我們比以前的架構進一步,并嘗試在潛在代碼 z 中強加某些結構。特別地,我們希望架構能夠在完全監督的場景中將類別信息與字跡風格分開。為此,我們將以前的架構擴展到下圖中。我們將潛在維度分為兩部分:第一個 z 類似于上一個例子;隱藏代碼的第二部分現在是一個獨熱向量(one-hot vector)y 表示饋送到自編碼器的數字的身份。
監督式對抗自編碼器架構。
在該設置中,解碼器使用獨熱向量 y 和隱藏代碼 z 來重建原始圖像。編碼器的任務是編寫 z 中的風格信息。在下面的圖片中,我們可以看到用 10000 個標簽的 MNIST 樣本來訓練這個架構的結果。該圖顯示了重建圖像,其中對于每行,隱藏代碼 z 被固定為特定值,類別標簽 y 的范圍從 0 到 9。字跡風格在列的維度上有效地保存了下來。
通過探索潛在代碼 y 并保持 z 從左到右固定重建圖像。
半監督式方法
作為我們最后一個實驗,我們找到一種替代方法來獲得類似的消糾纏結果,在這種情況下,我們只有很少的標簽信息樣本。我們可以修改之前的架構,使得 AAE 產生一個潛在的代碼,它由表示類別或標簽(使用 Softmax)的向量 y 和連續的潛在變量 z(使用線性層)連接組成。由于我們希望向量 y 表現為一個獨熱向量,我們通過使用第二個帶有判別器 Dcat 的對抗網絡迫使其遵從分類分布。編碼器現在是 q(z,y|x)。解碼器使用類別標簽和連續隱藏代碼重建圖像。
半監督式對抗自編碼器架構。
基于重建損失函數創建隱藏代碼和改進無需標簽信息的生成器和判別器網絡,未標記的數據通過這種方式改進編碼器以促進訓練過程。
用半監督式方法得到消糾纏結果。
值得注意的是,現在不僅可以通過較少標簽信息生成圖像,還可以通過查看潛在代碼 y 并選擇具有最高價值的圖像來分類我們沒有標簽的圖像。通過目前的設置,使用 100 個標簽樣本和 47000 個未標記的樣本,分類誤差約為 3%。
關于 GPU 訓練
最后,我們將在 Paperspace 平臺上為兩個不同 GPU 和 CPU 中的最后一個算法做一個訓練時間方面的簡短比較。即使這種架構不是很復雜,而且由很少的線性層組成,但是在使用 GPU 加速時,訓練時間的改善是巨大的。經過 500 epoch 的訓練時間,從 CPU 的近 4 小時降至使用 Nvidia Quadro M4000 的 9 分鐘,使用 Nvidia Quadro P5000 進一步下降到 6 分鐘。
有無 GPU 加速的訓練時間對比
更多資料
What is a variational autoencoder (https://jaan.io/what-is-variational-autoencoder-vae-tutorial) (Tutorial)
Auto-encoding Variational Bayes (https://arxiv.org/abs/1312.6114) (original paper)
Adversarial Autoencoders (https://arxiv.org/abs/1511.05644) (original paper)
Building Machines that Imagine and Reason: Principles and Applications of Deep Generative Models (http://videolectures.net/deeplearning2016_mohamed_generative_models/) (Video Lecture)