日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 人工智能 > pytorch >内容正文

pytorch

深度学习指南:在iOS平台上使用TensorFlow

發(fā)布時間:2025/3/15 pytorch 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 深度学习指南:在iOS平台上使用TensorFlow 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

在利用深度學(xué)習(xí)網(wǎng)絡(luò)進(jìn)行預(yù)測性分析之前,我們首先需要對其加以訓(xùn)練。目前市面上存在著大量能夠用于神經(jīng)網(wǎng)絡(luò)訓(xùn)練的工具,但TensorFlow無疑是其中極為重要的首選方案之一。

大家可以利用TensorFlow訓(xùn)練自己的機(jī)器學(xué)習(xí)模型,并利用這些模型完成預(yù)測性分析。訓(xùn)練通常由一臺極為強(qiáng)大的設(shè)備或者云端資源完成,但您可能想象不到的是,TensorFlow亦可以在iOS之上順利起效——只是存在一定局限性。

相關(guān)廠商內(nèi)容

一手實(shí)踐,摩拜產(chǎn)品研發(fā)負(fù)責(zé)人談?wù)効战祱F(tuán)隊那些事

業(yè)務(wù)與產(chǎn)品面面觀,Mobvista CTO談?wù)劶夹g(shù)與業(yè)務(wù)融合之道

技術(shù)領(lǐng)導(dǎo)者,我是如何打造自己的影響力?

如何抓住技術(shù)浪潮變革的紅利

數(shù)字化經(jīng)濟(jì)下技術(shù)領(lǐng)導(dǎo)者的洞察創(chuàng)新之路

相關(guān)贊助商

全球技術(shù)領(lǐng)導(dǎo)力峰會2017,6月30日-7月1日,上海·寶華萬豪酒店,精彩內(nèi)容搶先看

在今天的博文中,我們將共同了解TensorFlow背后的設(shè)計思路、如何利用其訓(xùn)練一套簡單的分類器,以及如何將上述成果引入您的iOS應(yīng)用。

在本示例中,我們將使用“根據(jù)語音與對話分析判斷性別”數(shù)據(jù)集以了解如何根據(jù)音頻記錄判斷語音為男聲抑或女聲。

獲取相關(guān)代碼:大家可以通過GitHub上的對應(yīng)項(xiàng)目獲取本示例的源代碼。

TensorFlow是什么,我們?yōu)楹涡枰右允褂?#xff1f;

TensorFlow是一套用于構(gòu)建計算性圖形,從而實(shí)現(xiàn)機(jī)器學(xué)習(xí)的軟件資源庫。

其它一些工具往往作用于更高級別的抽象層級。以Caffe為例,大家需要將不同類型的“層”進(jìn)行彼此互連,從而設(shè)計出一套神經(jīng)網(wǎng)絡(luò)。而iOS平臺上的BNNS與MPSCNN亦可實(shí)現(xiàn)類似的功能。

在TensorFlow當(dāng)中,大家亦可處理這些層,但具體處理深度將更為深入——甚至直達(dá)您算法中的各項(xiàng)計算流程。

大家可以將TensorFlow視為一套用于實(shí)現(xiàn)新型機(jī)器學(xué)習(xí)算法的工具集,而其它深度學(xué)習(xí)工具則用于幫助用戶使用這些算法。

當(dāng)然,這并不是說用戶需要在TensorFlow當(dāng)中從零開始構(gòu)建一切。TensorFlow擁有一整套可復(fù)用的構(gòu)建組件,同時囊括了Keras等負(fù)責(zé)為TensorFlow用戶提供大量便捷模塊的資源庫。

因此TensorFlow在使用當(dāng)中并不強(qiáng)制要求大家精通相關(guān)數(shù)學(xué)專業(yè)知識,當(dāng)然如果各位愿意自行構(gòu)建,TensorFlow也能夠提供相應(yīng)的工具。

利用邏輯回歸實(shí)現(xiàn)二元分類

在今天的博文當(dāng)中,我們將利用邏輯回歸(logistic regression)算法創(chuàng)建一套分類器。沒錯,我們將從零開始進(jìn)行構(gòu)建,因此請大家做好準(zhǔn)備——這可是項(xiàng)有點(diǎn)復(fù)雜的任務(wù)。所謂分類器,其基本工作原理是獲取輸入數(shù)據(jù),而后告知用戶該數(shù)據(jù)所歸屬的類別——或者種類。在本項(xiàng)目當(dāng)中,我們只設(shè)定兩個種類:男聲與女聲——也就是說,我們需要構(gòu)建的是一套二元分類器(binary classifier)。

備注:二元分類器屬于最簡單的一種分類器,但其基本概念與設(shè)計思路同用于區(qū)分成百上千種不同類別的分類器完全一致。因此,盡管我們在本份教程中不會太過深入,但相信大家仍然能夠從中一窺分類器設(shè)計的門徑。

在輸入數(shù)據(jù)方面,我們將使用包含20個數(shù)字朗讀語音、囊括多種聲學(xué)特性的給定錄音。我將在后文中對此進(jìn)行詳盡解釋,包括音頻頻率及其它相關(guān)信息。

在以下示意圖當(dāng)中,大家可以看到這20個數(shù)字全部接入一個名為sum的小框。這些連接擁有不同的weights(權(quán)重),對于分類器而言代表著這20個數(shù)字各自不同的重要程度。

以下框圖展示了這套邏輯分類器的起效原理:

在sum框當(dāng)中,輸入數(shù)據(jù)區(qū)間為x0到x19,且其對應(yīng)連接的權(quán)重w0到w19進(jìn)行直接相加。以下為一項(xiàng)常見的點(diǎn)積:

sum = x[0]*w[0] + x[1]*w[1] + x[2]*w[2] + ... + x[19]*w[19] + b

我們還在所謂bias(偏離)項(xiàng)的末尾加上了b。其僅僅代表另一個數(shù)字。

數(shù)組w中的權(quán)重與值b代表著此分類器所學(xué)習(xí)到的經(jīng)驗(yàn)。對該分類器進(jìn)行訓(xùn)練的過程,實(shí)際上是為了幫助其找到與w及b正確匹配的數(shù)字。最初,我們將首先將全部w與b設(shè)置為0。在數(shù)輪訓(xùn)練之后,w與b則將包含一組數(shù)字,分類器將利用這些數(shù)字將輸入語音中的男聲與女聲區(qū)分開來。為了能夠?qū)um轉(zhuǎn)化為一條概率值——其取值在0與1之間——我們在這里使用logistic sigmoid函數(shù):

y_pred = 1 / (1 + exp(-sum))

這條方程式看起來很可怕,但做法卻非常簡單:如果sum是一個較大正數(shù),則sigmoid函數(shù)返回1或者概率為100%; 如果sum是一個較大負(fù)數(shù),則sigmoid函數(shù)返回0。因此對于較大的正或者負(fù)數(shù),我們即可得出較為肯定的“是”或者“否”預(yù)測結(jié)論。

然而,如果sum趨近于0,則sigmoid函數(shù)會給出一個接近于50%的概率,因?yàn)槠錈o法確定預(yù)測結(jié)果。當(dāng)我們最初對分類器進(jìn)行訓(xùn)練時,其初始預(yù)期結(jié)果會因分類器本身訓(xùn)練尚不充分而顯示為50%,即對判斷結(jié)果并無信心。但隨著訓(xùn)練工作的深入,其給出的概率開始更趨近于1及0,即分類器對于結(jié)果更為肯定。

現(xiàn)在y_pred中包含的預(yù)測結(jié)果顯示,該語音為男聲的可能性更高。如果其概率高于0.5(或者50%),則我們認(rèn)為語音為男聲; 相反則為女聲。

這即是我們這套利用邏輯回歸實(shí)現(xiàn)的二元分類器的基本設(shè)計原理。輸入至該分類器的數(shù)據(jù)為一段對20個數(shù)字進(jìn)行朗讀的音頻記錄,我們會計算出一條權(quán)重sum并應(yīng)用sigmoid函數(shù),而我們獲得的輸出概率指示朗讀者應(yīng)為男性。

然而,我們?nèi)匀恍枰⒂糜谟?xùn)練該分類器的機(jī)制,而這時就需要請出今天的主角——TensorFlow了。

在TensorFlow中實(shí)現(xiàn)此分類器

要在TensorFlow當(dāng)中使用此分類器,我們需要首先將其設(shè)計轉(zhuǎn)化為一套計算圖(computational graph)。一項(xiàng)計算圖由多個負(fù)責(zé)執(zhí)行計算的節(jié)點(diǎn)組成,且輸入數(shù)據(jù)會在各節(jié)點(diǎn)之間往來流通。

我們這套邏輯回歸算法的計算圖如下所示:

看起來與之前給出的示意圖存在一定區(qū)別,但這主要是由于此處的輸入內(nèi)容x不再是20個獨(dú)立的數(shù)字,而是一個包含有20個元素的向量。在這里,權(quán)重由矩陣W表示。因此,之前得出的點(diǎn)積也在這里被替換成了一項(xiàng)矩陣乘法。

另外,本示意圖中還包含一項(xiàng)輸入內(nèi)容y。其用于對分類器進(jìn)行訓(xùn)練并驗(yàn)證其運(yùn)行效果。我們在這里使用的數(shù)據(jù)集為一套包含3168條example語音記錄的集合,其中每條示例記錄皆被明確標(biāo)記為男聲或女聲。這些已知男聲或女聲結(jié)果亦被稱為該數(shù)據(jù)集的label(標(biāo)簽),并作為我們交付至y的輸入內(nèi)容。

為了訓(xùn)練我們的分類器,這里需要將一條示例加載至x當(dāng)中并允許該計算圖進(jìn)行預(yù)測:即語音到底為男聲抑或是女聲?由于初始權(quán)重值全部為0,因此該分類器很可能給出錯誤的預(yù)測。我們需要一種方法以計算其錯誤的“具體程度”,而這一目標(biāo)需要通過loss函數(shù)實(shí)現(xiàn)。Loss函數(shù)會將預(yù)測結(jié)果y_pred與正確輸出結(jié)果y進(jìn)行比較。

在將loss函數(shù)提供給訓(xùn)練示例后,我們利用一項(xiàng)被稱為backpropagation(反向傳播)的技術(shù)通過該計算圖進(jìn)行回溯,旨在根據(jù)正確方向?qū)與b的權(quán)重進(jìn)行小幅調(diào)整。如果預(yù)測結(jié)果為男聲但實(shí)際結(jié)果為女聲,則權(quán)重值即會稍微進(jìn)行上調(diào)或者下調(diào),從而在下一次面對同樣的輸入內(nèi)容時增加將其判斷為“女聲”的概率。

這一訓(xùn)練規(guī)程會利用該數(shù)據(jù)集中的全部示例進(jìn)行不斷重復(fù)再重復(fù),直到計算圖本身已經(jīng)獲得了最優(yōu)權(quán)重集合。而負(fù)責(zé)衡量預(yù)測結(jié)果錯誤程度的loss函數(shù)則因此隨時間推移而變低。

反向傳播在計算圖的訓(xùn)練當(dāng)中扮演著極為重要的角色,但我們還需要加入一點(diǎn)數(shù)學(xué)手段讓結(jié)果更為準(zhǔn)確。而這也正是TensorFlow的專長所在:我們只要將全部“前進(jìn)”操作表達(dá)為計算圖當(dāng)中的節(jié)點(diǎn),其即可自動意識到“后退”操作代表的是反向傳播——我們完全無需親自進(jìn)行任何數(shù)學(xué)運(yùn)算。太棒了!

Tensorflow到底是什么?

在以上計算圖當(dāng)中,數(shù)據(jù)流向?yàn)閺淖笾劣?#xff0c;即代表由輸入到輸出。而這正是TensorFlow中“流(flow)”的由來。不過Tensor又是什么?

Tensor一詞本義為張量,而此計算圖中全部數(shù)據(jù)流皆以張量形式存在。所謂張量,其實(shí)際代表的就是一個n維數(shù)組。我曾經(jīng)提到W是一項(xiàng)權(quán)重矩陣,但從TensorFlow的角度來看,其實(shí)際上屬于一項(xiàng)二階張量——換言之,一個二組數(shù)組。

一個標(biāo)量代表一個零階張量。

  • 一個向量代表一個一階張量。
  • 一個矩陣代表一個二階張量。
  • 一個三維數(shù)組代表一個三階張量。
  • 之后以此類推……

這就是Tensor的全部含義。在卷積神經(jīng)網(wǎng)絡(luò)等深度學(xué)習(xí)方案當(dāng)中,大家會需要與四維張量打交道。但本示例中提到的邏輯分類器要更為簡單,因此我們在這里最多只涉及到二階張量——即矩陣。

我之前還提到過,x代表一個向量——或者說一個一階張量——但接下來我們同樣將其視為一個矩陣。y亦采用這樣的處理方式。如此一來,我們即可將數(shù)據(jù)庫組視為整體對其loss進(jìn)行計算。

一條簡單的示例(example)語音內(nèi)包含20個數(shù)據(jù)元素。如果大家將全部3168條示例加載至x當(dāng)中,則x會成為一個3168 x 20的矩陣。再將x與W相乘,則得出的結(jié)果y_pred為一個3168 x 1的矩陣。具體來講,y_pred代表的是為數(shù)據(jù)集中的每條語音示例提供一項(xiàng)預(yù)測結(jié)論。

通過將我們的計算圖以矩陣/張量的形式進(jìn)行表達(dá),我們可以一次性對多個示例進(jìn)行預(yù)測。

安裝TensorFlow

好的,以上是本次教程的理論基礎(chǔ),接下來進(jìn)入實(shí)際操作階段。

我們將通過Python使用TensorFlow。大家的Mac設(shè)備可能已經(jīng)安裝有某一Python版本,但其版本可能較為陳舊。在本教程中,我使用的是Python 3.6,因此大家最好也能安裝同一版本。

安裝Python 3.6非常簡單,大家只需要使用Homebrew軟件包管理器即可。如果大家還沒有安裝homebrew,請點(diǎn)擊此處參閱相關(guān)指南。

接下來打開終端并輸入以下命令,以安裝Python的最新版本:

brew install python3

Python也擁有自己的軟件包管理器,即pip,我們將利用它安裝我們所需要的其它軟件包。在終端中輸入以下命令:

pip3 install numpy pip3 install scipy pip3 install scikit-learn pip3 install pandas pip3 install tensorflow

除了TensorFlow之外,我們還需要安裝NumPy、SciPy、pandas以及scikit-learn:

NumPy是一套用于同n級數(shù)組協(xié)作的庫。聽起來耳熟嗎?NumPy并非將其稱為張量,但之前提到了數(shù)組本身就是一種張量。TensorFlow Python API就建立在NumPy基礎(chǔ)之上。
  • SciPy是一套用于數(shù)值計算的庫。其它一些軟件包的起效需要以之為基礎(chǔ)。
  • pandas負(fù)責(zé)數(shù)據(jù)集的加載與清理工作。
  • scikit-learn在某種意義上可以算作TensorFlow的競爭對手,因?yàn)槠渫瑯邮且惶子糜跈C(jī)器學(xué)習(xí)的庫。我們之所以在本項(xiàng)目中加以使用,是因?yàn)樗邆涠囗?xiàng)便利的功能。由于TensorFlow與scikit-learn皆使用NumPy數(shù)組,因?yàn)槎吣軌蝽槙硨?shí)現(xiàn)協(xié)作。

實(shí)際上,大家無需pandas與scikit-learn也能夠使用TensorFlow,但二者確實(shí)能夠提供便捷功能,而且每一位數(shù)據(jù)科學(xué)家也都樂于加以使用。

如大家所知,這些軟件包將被安裝在/usr/local/lib/python3.6/site-packages當(dāng)中。如果大家需要查看部分未被公布在其官方網(wǎng)站當(dāng)中的TensorFlow源代碼,則可以在這里找到。

備注:pip應(yīng)會為您的系統(tǒng)自動安裝TensorFlow的最佳版本。如果大家希望安裝其它版本,則請點(diǎn)擊此處參閱官方安全指南。另外,大家也可以利用源代碼自行構(gòu)建TensorFlow,這一點(diǎn)我們稍后會在面向iOS構(gòu)建TensorFlow部分中進(jìn)行說明。

下面我們進(jìn)行一項(xiàng)快速測試,旨在確保一切要素都已經(jīng)安裝就緒。利用以下內(nèi)容創(chuàng)建一個新的tryit.py文件:

import tensorflow as tf a = tf.constant([1, 2, 3]) b = tf.constant([4, 5, 6]) sess = tf.Session(config=tf.ConfigProto(log_device_placement=True)) print(sess.run(a + b))

而后通過終端運(yùn)行這套腳本:

python3 tryit.py

其會顯示一些與TensorFlow運(yùn)行所在設(shè)備相關(guān)的調(diào)試信息(大多為CPU信息,但如果您所使用的Mac設(shè)備配備有英偉達(dá)GPU,亦可能提供GPU信息)。最終結(jié)果顯示為:

[5 7 9]

這里代表的是兩個向量a與b的加和。另外,大家可能還會看到以下信息:

W tensorflow/core/platform/cpu_feature_guard.cc:45] The TensorFlow library? wasn't compiled to use SSE4.1 instructions, but these are available on your? machine and could speed up CPU computations.

如果出現(xiàn)上述內(nèi)容,則代表您在系統(tǒng)當(dāng)中安裝的TensorFlow并非當(dāng)前CPU的最優(yōu)適配版本。修復(fù)方法之一是利用源代碼自行構(gòu)建TensorFlow,因?yàn)檫@允許大家對全部選項(xiàng)加以配置。但在本示例當(dāng)中,由于其不會造成什么影響,因此直接忽略即可。

深入觀察訓(xùn)練數(shù)據(jù)集

要訓(xùn)練分類器,我們自然需要數(shù)據(jù)。

在本項(xiàng)目當(dāng)中,我們使用來自Kory Becker的“根據(jù)語音判斷性別”數(shù)據(jù)集。為了能夠讓這份教程能夠與TensorFlow指南上的MNIST數(shù)字化識別有所不同,這里我決定在Kaggle.com上尋找數(shù)據(jù)集,并最終選定了這一套。

那么我們到底該如何立足音頻實(shí)現(xiàn)性別判斷?下載該數(shù)據(jù)集并打開voice.csv文件之后,大家會看到其中包含著一排排數(shù)字:

我們首先需要強(qiáng)調(diào)這一點(diǎn),這里列出的并非實(shí)際音頻數(shù)據(jù)!相反,這些數(shù)字代表著語音記錄當(dāng)中的不同聲學(xué)特征。這些屬性或者特征由一套腳本自音頻記錄中提取得出,并被轉(zhuǎn)化為這個CSV文件。具體提取方式并不屬于本篇文章希望討論的范疇,但如果大家感興趣,則可點(diǎn)擊此處查閱其原始R源代碼。

這套數(shù)據(jù)集中包含3168項(xiàng)示例(每項(xiàng)示例在以上表格中作為一行),且基本半數(shù)為男聲錄制、半數(shù)為女聲錄制。每一項(xiàng)示例中存在20項(xiàng)聲學(xué)特征,例如:

以kHz為單位的平均頻率

  • 頻率的標(biāo)準(zhǔn)差
  • 頻譜平坦度
  • 頻譜熵
  • 峰度
  • 聲學(xué)信號中測得的最大基頻
  • 調(diào)制指數(shù)
  • 等等……

別擔(dān)心,雖然我們并不了解其中大多數(shù)條目的實(shí)際意義,但這不會影響到本次教程。我們真正需要 關(guān)心的是如何利用這些數(shù)據(jù)訓(xùn)練自己的分類器,從而立足于上述特征確保其有能力區(qū)分男性與女性的語音。

如果大家希望在一款應(yīng)用程序當(dāng)中使用此分類器,從而通過錄音或者來自麥克風(fēng)的音頻信息檢測語音性別,則首先需要從此類音頻數(shù)據(jù)中提取聲學(xué)特征。在擁有了這20個數(shù)字之后,大家即可對其分類器加以訓(xùn)練,并利用其判斷語音內(nèi)容為男聲還是女聲。

因此,我們的分類器并不會直接處理音頻記錄,而是處理從記錄中提取到的聲學(xué)特征。

備注:我們可以以此為起點(diǎn)了解深度學(xué)習(xí)與邏輯回歸等傳統(tǒng)算法之間的差異。我們所訓(xùn)練的分類器無法學(xué)習(xí)非常復(fù)雜的內(nèi)容,大家需要在預(yù)處理階段提取更多數(shù)據(jù)特征對其進(jìn)行幫助。在本示例的特定數(shù)據(jù)集當(dāng)中,我們只需要考慮提取音頻記錄中的音頻數(shù)據(jù)。

深度學(xué)習(xí)最酷的能力在于,大家完全可以訓(xùn)練一套神經(jīng)網(wǎng)絡(luò)來學(xué)習(xí)如何自行提取這些聲學(xué)特征。如此一來,大家不必進(jìn)行任何預(yù)處理即可利用深度學(xué)習(xí)系統(tǒng)采取原始音頻作為輸入內(nèi)容,并從中提取任何其認(rèn)為重要的聲學(xué)特征,而后加以分類。

這當(dāng)然也是一種有趣的深度學(xué)習(xí)探索方向,但并不屬于我們今天討論的范疇,因此也許日后我們將另開一篇文章單獨(dú)介紹。

建立一套訓(xùn)練集與測試集

在前文當(dāng)中,我提到過我們需要以如下步驟對分類器進(jìn)行訓(xùn)練:

向其交付來自數(shù)據(jù)集的全部示例。

  • 衡量預(yù)測結(jié)果的錯誤程度。
  • 根據(jù)loss調(diào)整權(quán)重。

事實(shí)證明,我們不應(yīng)利用全部數(shù)據(jù)進(jìn)行訓(xùn)練。我們只需要其中的特定一部分?jǐn)?shù)據(jù)——即測試集——從而評估分類器的實(shí)際工作效果。因此,我們將把整體數(shù)據(jù)集拆分為兩大部分:訓(xùn)練集,用于對分類器進(jìn)行訓(xùn)練; 測試集,用于了解該分類器的預(yù)測準(zhǔn)確度。

為了將數(shù)據(jù)拆分為訓(xùn)練集與測試集,我創(chuàng)建了一套名為split_data.py的Python腳本,其內(nèi)容如下所示:

import numpy as np # 1 import pandas as pd df = pd.read_csv("voice.csv", header=0) # 2 labels = (df["label"] == "male").values * 1 # 3 labels = labels.reshape(-1, 1) # 4 del df["label"] # 5 data = df.values # 6 from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split(data, labels, test_size=0.3, random_state=123456) np.save("X_train.npy", X_train) # 7 np.save("X_test.npy", X_test) np.save("y_train.npy", y_train) np.save("y_test.npy", y_test)

下面我們將分步驟了解這套腳本的工作方式:

  • 首先導(dǎo)入NumPy與pandas軟件包。Pandas能夠輕松實(shí)現(xiàn)CSV文件的加載,并對數(shù)據(jù)進(jìn)行預(yù)處理。
  • 利用pandas從voice.csv加載數(shù)據(jù)集并將其作為dataframe。此對象在很大程度上類似于電子表格或者SQL表。
  • 這里的label列包含有該數(shù)據(jù)集的各項(xiàng)標(biāo)簽:即該示例為男聲或者女聲。在這里,我們將這些標(biāo)簽提取進(jìn)一個新的NumPy數(shù)組當(dāng)中。各原始標(biāo)簽為文本形式,但我們將其轉(zhuǎn)化為數(shù)字形式,其中1=男聲,0=女聲。(這里的數(shù)字賦值方式可任意選擇,在二元分類器中,我們通常使用1表示‘正’類,或者說我們試圖檢測的類。)
  • 這里創(chuàng)建的新labels數(shù)組是一套一維數(shù)組,但我們的TensorFlow腳本則需要一套二維張量,其中3168行中每一行皆對應(yīng)一列。因此我們需要在這里對數(shù)組進(jìn)行“重塑”,旨在將其轉(zhuǎn)化為二維形式。這不會對內(nèi)存中的數(shù)據(jù)產(chǎn)生影響,而僅變化NumPy對數(shù)據(jù)的解釋方式。
  • 在完成label列之后,我們將其從dataframe當(dāng)中移除,這樣我們就只剩下20項(xiàng)用于描述輸入內(nèi)容的特征。我們還將把該dataframe轉(zhuǎn)換為一套常規(guī)NumPy數(shù)組。
  • 這里,我們利用來自scikit-learn的一項(xiàng)helper函數(shù)將data與labels數(shù)組拆分為兩個部分。這種對數(shù)據(jù)集內(nèi)各示例進(jìn)行隨機(jī)洗牌的操作基于random_state,即一類隨機(jī)生成器。無論具體內(nèi)容為何,但只要青筋相同內(nèi)容,我們即創(chuàng)造出了一項(xiàng)可重復(fù)進(jìn)行的實(shí)驗(yàn)。
  • 最后,將四項(xiàng)新的數(shù)組保存為NumPy的二進(jìn)制文件格式。現(xiàn)在我們已經(jīng)擁有了一套訓(xùn)練集與一套測試集!
  • 大家也可以進(jìn)行額外的一些預(yù)處理對腳本中的數(shù)據(jù)進(jìn)行調(diào)整,例如對特征進(jìn)行擴(kuò)展,從而使其擁有0均值及相等的方差,但由于本次示例項(xiàng)目比較簡單,所以并無深入調(diào)整的必要。

    利用以下命令在終端中運(yùn)行這套腳本:

    python3 split_data.py

    這將給我們帶來4個新文件,其中包含有訓(xùn)練救命(X_train.npy)、這些示例的對應(yīng)標(biāo)簽(y_train.npy)、測試示例(X_test.npy)及其對應(yīng)標(biāo)簽(y_test.npy)。

    備注:大家可能想了解為什么這些變量名稱為何有些是大寫,有些是小寫。在數(shù)學(xué)層面來看,矩陣通常以大寫表示而向量則以小寫表示。在我們的腳本中,X代表一個矩陣,y代表一個向量。這是一種慣例,大部分機(jī)器學(xué)習(xí)代碼中皆照此辦理。

    建立計算圖

    現(xiàn)在我們已經(jīng)對數(shù)據(jù)進(jìn)行了梳理,而后即可編寫一套腳本以利用TensorFlow對這套邏輯分類器進(jìn)行訓(xùn)練。這套腳本名為train.py。為了節(jié)省篇幅,這里就不再列出腳本的具體內(nèi)容了,大家可以點(diǎn)擊此處在GitHub上進(jìn)行查看。

    與往常一樣,我們首先需要導(dǎo)入需要的軟件包。在此之后,我們將訓(xùn)練數(shù)據(jù)加載至兩個NumPy數(shù)組當(dāng)中,即X_train與y_train。(我們在本腳本中不會使用測試數(shù)據(jù)。)

    import numpy as np import tensorflow as tf X_train = np.load("X_train.npy") y_train = np.load("y_train.npy")

    現(xiàn)在我們可以建立自己的計算圖。首先,我們?yōu)槲覀兊妮斎雰?nèi)容x與y定義所謂placeholders(占位符):

    num_inputs = 20 num_classes = 1 with tf.name_scope("inputs"): x = tf.placeholder(tf.float32, [None, num_inputs], name="x-input") y = tf.placeholder(tf.float32, [None, num_classes], name="y-input")

    其中tf.name_scope("...")可用于對該計算圖中的不同部分按不同范圍進(jìn)行分組,從而簡化對計算圖內(nèi)容的理解。我們將x與y添加至“inputs”范圍之內(nèi)。我們還將為其命名,分別為“x-input”與“y-input”,這樣即可在隨后輕松加以引用。

    大家應(yīng)該還記得,每條輸入示例都是一個包含20項(xiàng)元素的向量。每條示例亦擁有一個標(biāo)簽(1代表男聲,0代表女聲)。我之前還提到過,我們可以將全部示例整合為一個矩陣,從而一次性對其進(jìn)行全面計算。正因?yàn)槿绱?#xff0c;我們這里將x與y定義為二維張量:x擁有[None, 20]維度,而y擁有[None, 1]維度。

    其中的None代表第一項(xiàng)維度為靈活可變且目前未知。在訓(xùn)練集當(dāng)中,我們將2217條示例導(dǎo)入x與y; 而在測試集中,我們引入951條示例。現(xiàn)在,TensorFlow已經(jīng)了解了我們的輸入內(nèi)容,接下來對分類器的parameters(參數(shù))進(jìn)行定義:

    with tf.name_scope("model"): W = tf.Variable(tf.zeros([num_inputs, num_classes]), name="W") b = tf.Variable(tf.zeros([num_classes]), name="b")

    其中的張量W包含有分類器將要學(xué)習(xí)的權(quán)重(這是一個20 x 1矩陣,因?yàn)槠渲邪?0條輸入特征與1條輸出結(jié)果),而b則包含偏離值。這二者被聲明為TensorFlow變量,意味著二者可在反向傳播過程當(dāng)中實(shí)現(xiàn)更新。

    在一切準(zhǔn)備就緒之后,我們可以對作為邏輯回歸分類器核心的計算流程進(jìn)行聲明了:

    y_pred = tf.sigmoid(tf.matmul(x, W) + b)

    這里將x與W進(jìn)行相乘,同時加上偏離值b,而后取其邏輯型成長曲線(logistic sigmoid)。如此一來,y_pred中的結(jié)果即根據(jù)x內(nèi)音頻數(shù)據(jù)的描述特性而被判斷為男聲的概率。

    備注:以上代碼目前實(shí)際還不會做出任何計算——截至目前,我們還只是構(gòu)建起了必要的計算圖。這一行單純是將各節(jié)點(diǎn)添加至計算圖當(dāng)中以作為矩陣乘法(tf.matmul)、加法(+)以及sigmoid函數(shù)(tf.sigmoid)。在完成整體計算圖的構(gòu)建之后,我們方可創(chuàng)建TensorFlow會話并利用實(shí)際數(shù)據(jù)加以運(yùn)行。

    到這里任務(wù)還未完成。為了訓(xùn)練這套模型,我們需要定義一項(xiàng)loss函數(shù)。對于一套二元邏輯回歸分類器,我們需要使用log loss,幸運(yùn)的是TensorFlow本身內(nèi)置有一項(xiàng)log_loss()函數(shù)可供直接使用:

    with tf.name_scope("loss-function"):???? loss = tf.losses.log_loss(labels=y, predictions=y_pred)???? loss += regularization * tf.nn.l2_loss(W)

    其中的log_loss計算圖節(jié)點(diǎn)作為輸入內(nèi)容y,我們會獲取與之相關(guān)的示例標(biāo)簽并將其與我們的預(yù)測結(jié)果y_pred進(jìn)行比較。以數(shù)字顯示的結(jié)果即為loss值。

    在剛開始進(jìn)行訓(xùn)練時,所有示例的預(yù)測結(jié)果y_pred皆將為0.5(或者50%男聲),這是因?yàn)榉诸惼鞅旧砩胁磺宄绾潍@得正確答案。其初始loss在經(jīng)-1n(0.5)計算后得出為0.693146。而在訓(xùn)練的推進(jìn)當(dāng)中,其loss值將變得越來越小。

    第二行用于計算loss值與所謂L2 regularization(正則化)的加值。這是為了防止過度擬合阻礙分類器對訓(xùn)練數(shù)據(jù)的準(zhǔn)確記憶。這一過程比較簡單,因?yàn)槲覀兊姆诸惼鳌皟?nèi)存”只包含20項(xiàng)權(quán)重值與偏離值。不過正則化本身是一種常見的機(jī)器學(xué)習(xí)技術(shù),因此在這里必須一提。

    這里的regularization值為另一項(xiàng)占位符:

    with tf.name_scope("hyperparameters"):???? regularization = tf.placeholder(tf.float32, name="regularization")???? learning_rate = tf.placeholder(tf.float32, name="learning-rate")

    我們還將利用占位符定義我們的輸入內(nèi)容x與y,不過二者的作用是定義hyperparameters。

    Hyperparameters允許大家對這套模型及其具體訓(xùn)練方式進(jìn)行配置。其之所以被稱為“超”參數(shù),是因?yàn)榕c常見的W與b參數(shù)不同,其并非由模型自身所學(xué)習(xí)——大家需要自行將其設(shè)置為適當(dāng)?shù)闹怠?/p>

    其中的learning_rate超參數(shù)負(fù)責(zé)告知優(yōu)化器所應(yīng)采取的調(diào)整幅度。該優(yōu)化器(optimizer)負(fù)責(zé)執(zhí)行反向傳播:其會提取loss值并將其傳遞回計算圖以確定需要對權(quán)重值與偏離值進(jìn)行怎樣的調(diào)整。這里可以選擇的優(yōu)化器方案多種多樣,而我們使用的為ADAM:

    with tf.name_scope("train"):???? optimizer = tf.train.AdamOptimizer(learning_rate)???? train_op = optimizer.minimize(loss)

    其能夠在計算圖當(dāng)中創(chuàng)建一個名為train_op的節(jié)點(diǎn)。我們稍后將運(yùn)行此節(jié)點(diǎn)以訓(xùn)練分類器。為了確定該分類器的運(yùn)行效果,我們還需要在訓(xùn)練當(dāng)中偶爾捕捉快照并計算其已經(jīng)能夠在訓(xùn)練集當(dāng)中正確預(yù)測多少項(xiàng)示例。訓(xùn)練集的準(zhǔn)確性并非分類器運(yùn)行效果的最終檢驗(yàn)標(biāo)準(zhǔn),但對其進(jìn)行追蹤能夠幫助我們在一定程度上把握訓(xùn)練過程與預(yù)測準(zhǔn)確性趨勢。具體來講,如果越是訓(xùn)練結(jié)果越差,那么一定是出了什么問題!

    下面我們?yōu)橐粋€計算圖節(jié)點(diǎn)定義計算精度:

    with tf.name_scope("inference"): inference = tf.to_float(y_pred > 0.5, name="inference")

    我們可以運(yùn)行其中的accuracy節(jié)點(diǎn)以查看有多少個示例得到了正確預(yù)測。大家應(yīng)該還記得,y_pred中包含一項(xiàng)介于0到1之間的概率。通過進(jìn)行tf.to_float(y_pred > 0.5),若預(yù)測結(jié)果為女聲則返回值為0,若預(yù)測結(jié)果為男聲則返回值為1。我們可以將其與y進(jìn)行比較,y當(dāng)中包含有正確值。而精度值則代表著正確預(yù)測數(shù)量除以預(yù)測總數(shù)。

    在此之后,我們將利用同樣的accuracy節(jié)點(diǎn)處理測試集,從而了解這套分類器的實(shí)際工作效果。

    另外,我們還需要定義另外一個節(jié)點(diǎn)。此節(jié)點(diǎn)用于對我們尚無對應(yīng)標(biāo)簽的數(shù)據(jù)進(jìn)行預(yù)測:

    with tf.name_scope("inference"):???? inference = tf.to_float(y_pred > 0.5, name="inference")

    為了將這套分類器引入應(yīng)用,我們還需要記錄幾個語音文本詞匯,對其進(jìn)行分析以提取20項(xiàng)聲學(xué)特征,而后再將其交付至分類器。由于處理內(nèi)容屬于全新數(shù)據(jù),而非來自訓(xùn)練或者測試集的數(shù)據(jù),因此我們顯然不具備與之相關(guān)的標(biāo)簽。大家只能將數(shù)據(jù)直接交付至分類器,并希望其能夠給出正確的預(yù)測結(jié)果。而inference節(jié)點(diǎn)的作用也正在于此。

    好的,我們已經(jīng)投入了大量精力來構(gòu)建這套計算圖。現(xiàn)在我們希望利用訓(xùn)練集對其進(jìn)行實(shí)際訓(xùn)練。

    訓(xùn)練分類器

    訓(xùn)練通常以無限循環(huán)的方式進(jìn)行。不過對于這套簡單的邏輯分類器,這種作法顯然有點(diǎn)夸張——因?yàn)槠洳坏揭环昼娂纯赏瓿捎?xùn)練。但對于深層神經(jīng)網(wǎng)絡(luò),我們往往需要數(shù)小時甚至數(shù)天時間進(jìn)行腳本運(yùn)行——直到其獲得令人滿意的精度或者您開始失去耐心。

    以下為train.py當(dāng)中訓(xùn)練循環(huán)的第一部分:

    with tf.Session() as sess: tf.train.write_graph(sess.graph_def, checkpoint_dir, "graph.pb", False)sess.run(init)step = 0 while True: # here comes the training code/pre>

    我們首先創(chuàng)建一個新的TensorFlow session(會話)對象。要運(yùn)行該計算圖,大家需要建立一套會話。調(diào)用sess.run(init)會將W與b全部重設(shè)為0。

    我們還需要將該計算圖寫入一個文件。我們將之前創(chuàng)建的全部節(jié)點(diǎn)序列至/tmp/voice/graph.pb文件當(dāng)中。我們之后需要利用此計算圖定義以立足測試集進(jìn)行分類器運(yùn)行,并嘗試將該訓(xùn)練后的分類器引入iOS應(yīng)用。

    在while True:循環(huán)當(dāng)中,我們使用以下內(nèi)容:

    perm = np.arange(len(X_train)) np.random.shuffle(perm) X_train = X_train[perm] y_train = y_train[perm]

    首先,我們對訓(xùn)練示例進(jìn)行隨機(jī)洗牌。這一點(diǎn)非常重要,因?yàn)榇蠹耶?dāng)然不希望分類器根據(jù)示例的具體順序進(jìn)行判斷——而非根據(jù)其聲學(xué)特征進(jìn)行判斷。

    接下來是最重要的環(huán)節(jié):我們要求該會話運(yùn)行train_op節(jié)點(diǎn)。其將在計算圖之上運(yùn)行一次訓(xùn)練:

    feed = {x: X_train, y: y_train, learning_rate: 1e-2, regularization: 1e-5} sess.run(train_op, feed_dict=feed)

    在運(yùn)行sess.run()時,大家還需要提供一套饋送詞典。其將負(fù)責(zé)告知TensorFlow當(dāng)前占位符節(jié)點(diǎn)的實(shí)際值。

    由于這只是一套非常簡單的分類器,因此我們將始終一次性對全部訓(xùn)練集進(jìn)行訓(xùn)練,所以這里我們將X_train數(shù)組引入占位符x并將y_train數(shù)組引入占位符y。(對于規(guī)模更大的數(shù)據(jù)集,大家可以先從小批數(shù)據(jù)內(nèi)容著手,例如將示例數(shù)量設(shè)定為100到1000之間。)

    到這里,我們的操作就階段性結(jié)束了。由于我們使用了無限循環(huán),因此train_op節(jié)點(diǎn)會反復(fù)再反復(fù)加以執(zhí)行。而在每一次迭代時,其中的反向傳播機(jī)制都會對權(quán)重值W與b作出小幅調(diào)整。隨著時間推移,這將令權(quán)重值逐步趨近于最優(yōu)值。

    我們當(dāng)然有必要了解訓(xùn)練進(jìn)度,因此我們需要經(jīng)常性地輸出進(jìn)度報告(在本示例項(xiàng)目中,每進(jìn)行1000次訓(xùn)練即輸出一次結(jié)果):

    if step % print_every == 0: train_accuracy, loss_value = sess.run([accuracy, loss], feed_dict=feed) print("step: %4d, loss: %.4f, training accuracy: %.4f" % \ (step, loss_value, train_accuracy))

    這一次我們不再運(yùn)行train_op節(jié)點(diǎn),而是運(yùn)行accuracy與loss兩個節(jié)點(diǎn)。我們使用同樣的饋送詞典,這樣accuracy與loss都會根據(jù)訓(xùn)練集進(jìn)行計算。正如之前所提到,訓(xùn)練集中的較高預(yù)測精度并不代表分類器能夠在處理測試集時同樣擁有良好表現(xiàn),但大家當(dāng)然希望隨著訓(xùn)練的進(jìn)行其精度值不斷提升。與此同時,loss值則應(yīng)不斷下降。

    另外,我們還需要時不時保存一份checkpoint:

    if step % save_every == 0: checkpoint_file = os.path.join(checkpoint_dir, "model") saver.save(sess, checkpoint_file) print("*** SAVED MODEL ***")

    其會獲取分類器當(dāng)前已經(jīng)學(xué)習(xí)到的W與b值,并將其保存為一個checkpoint文件。此checkpoint可供我們參閱,并判斷分類器是否已經(jīng)可以轉(zhuǎn)而處理測試集。該checkpoinit文件同樣被保存在/tmp/voice/目錄當(dāng)中。

    使用以下命令在終端中運(yùn)行該訓(xùn)練腳本:

    python3 train.py

    輸出結(jié)果應(yīng)如下所示:

    Training set size: (2217, 20) Initial loss: 0.693146 step:??? 0, loss: 0.7432, training accuracy: 0.4754 step: 1000, loss: 0.4160, training accuracy: 0.8904 step: 2000, loss: 0.3259, training accuracy: 0.9170 step: 3000, loss: 0.2750, training accuracy: 0.9229 step: 4000, loss: 0.2408, training accuracy: 0.9337 step: 5000, loss: 0.2152, training accuracy: 0.9405 step: 6000, loss: 0.1957, training accuracy: 0.9553 step: 7000, loss: 0.1819, training accuracy: 0.9594 step: 8000, loss: 0.1717, training accuracy: 0.9635 step: 9000, loss: 0.1652, training accuracy: 0.9666 *** SAVED MODEL *** step: 10000, loss: 0.1611, training accuracy: 0.9702 step: 11000, loss: 0.1589, training accuracy: 0.9707 . . .

    當(dāng)發(fā)現(xiàn)loss值不再下降時,就稍等一下直到看到下一條*** SAVED MODEL ***信息,這時按下Ctrl+C以停止訓(xùn)練。

    在超參數(shù)設(shè)置當(dāng)中,我選擇了正規(guī)化與學(xué)習(xí)速率,大家應(yīng)該會看到其訓(xùn)練集的準(zhǔn)確率已經(jīng)達(dá)到約97%,而loss值則約為0.157。(如果大家在饋送詞典中將regularization設(shè)置為0,則loss甚至還能夠進(jìn)一步降低。)

    實(shí)際效果如何?

    在完成對分類器的訓(xùn)練之后,接下來就是對其進(jìn)行測試以了解它在實(shí)踐當(dāng)中的運(yùn)行效果。大家需要使用未在訓(xùn)練中涉及過的數(shù)據(jù)完成這項(xiàng)測試。正因?yàn)槿绱?#xff0c;我們才在此前將數(shù)據(jù)集拆分為訓(xùn)練集與測試集。

    我們將創(chuàng)建一套新的test.py腳本,負(fù)責(zé)加載計算圖定義以及測試集,并計算其正確預(yù)測的測試示例數(shù)量。這里我將只提供重要的部分,大家可以點(diǎn)擊此處查看完整腳本內(nèi)容。

    備注:測試集的結(jié)果精確度將始終低于訓(xùn)練集的結(jié)果精確度(后者為97%)。不過如果前者遠(yuǎn)低于后者,則大家應(yīng)對分類器進(jìn)行檢查并對訓(xùn)練流程進(jìn)行調(diào)整。我們預(yù)計測試集的實(shí)際結(jié)果應(yīng)該在95%左右。任何低于90%的精度結(jié)果都應(yīng)引起重視。

    與之前一樣,這套腳本會首先導(dǎo)入必要軟件包,包括來自scikit-learn的指標(biāo)包以輸出各類其它報告。當(dāng)然,這一次我們選擇加載測試集而不再是訓(xùn)練集。

    import numpy as np import tensorflow as tf from sklearn import metrics? X_test = np.load("X_test.npy") y_test = np.load("y_test.npy")

    為了計算測試集的結(jié)果精確度,我們?nèi)匀恍枰嬎銏D。不過這一次不再需要完整的計算圖,因?yàn)閠rain_op與loss兩個用于訓(xùn)練的節(jié)點(diǎn)這里不會被用到。大家當(dāng)然可以再次手動建立計算圖,但由于此前我們已經(jīng)將其保存在graph.pb文件當(dāng)中,因此這里直接加載即可。以下為相關(guān)代碼:

    with tf.Session() as sess:???? graph_file = os.path.join(checkpoint_dir, "graph.pb")???? with tf.gfile.FastGFile(graph_file, "rb") as f:???????? graph_def = tf.GraphDef()???????? graph_def.ParseFromString(f.read())???????? tf.import_graph_def(graph_def, name="")

    TensorFlow可能會將其數(shù)據(jù)保存為協(xié)議緩沖文件(擴(kuò)展名為.pb),因此我們可以使用部分helper代碼以加載此文件并將其作為計算圖導(dǎo)入至?xí)挳?dāng)中。

    接下來,我們需要從checkpoint文件處加載W與b的值:

    W = sess.graph.get_tensor_by_name("model/W:0")???? b = sess.graph.get_tensor_by_name("model/b:0")checkpoint_file = os.path.join(checkpoint_dir, "model")???? saver = tf.train.Saver([W, b])???? saver.restore(sess, checkpoint_file)

    正因?yàn)槿绱?#xff0c;我們需要將節(jié)點(diǎn)引入范圍并為其命名,從而利用get_tensor_by_name()輕松再次將其找到。如果大家沒有為節(jié)點(diǎn)提供明確的名稱,則可能需要認(rèn)真查閱計算圖定義才能找到TensorFlow為其默認(rèn)分配的名稱。

    我們還需要引用其它幾個節(jié)點(diǎn),特別是作為輸入內(nèi)容的x與y以及其它負(fù)責(zé)進(jìn)行預(yù)測的節(jié)點(diǎn):

    x = sess.graph.get_tensor_by_name("inputs/x-input:0")???? y = sess.graph.get_tensor_by_name("inputs/y-input:0")???? accuracy = sess.graph.get_tensor_by_name("score/accuracy:0")???? inference = sess.graph.get_tensor_by_name("inference/inference:0")

    好的,到這里我們已經(jīng)將計算圖重新加載至內(nèi)存當(dāng)中。我們還需要再次將分類器學(xué)習(xí)到的內(nèi)容加載至W與b當(dāng)中。現(xiàn)在我們終于可以測試分類器在處理其之前從未見過的數(shù)據(jù)時表現(xiàn)出的精確度了:

    feed = {x: X_test, y: y_test}???? print("Test set accuracy:", sess.run(accuracy, feed_dict=feed))

    上述代碼會運(yùn)行accuracy節(jié)點(diǎn)并利用來自X_test數(shù)組的聲學(xué)特征作為輸入內(nèi)容,同時使用來自y_test的標(biāo)簽進(jìn)行結(jié)果驗(yàn)證。

    備注:這一次,饋送詞典不再需要為learning_rate與regularization占位符指定任何值。我們只需要在accuracy節(jié)點(diǎn)上運(yùn)行計算圖的一部分,且此部分中并不包括這些占位符。

    我們還可以借助scikit-learn的幫助顯示其它一些報告:

    predictions = sess.run(inference, feed_dict={x: X_test}) print("Classification report:") print(metrics.classification_report(y_test.ravel(), predictions)) print("Confusion matrix:") print(metrics.confusion_matrix(y_test.ravel(), predictions))

    這一次,我們使用inference節(jié)點(diǎn)以獲取預(yù)測結(jié)果。由于inference只會計算預(yù)測結(jié)果而不會檢查其精確度,因此饋送詞典中僅需要包含輸入內(nèi)容x而不再需要y。

    運(yùn)行此腳本之后,大家應(yīng)該會看到類似于以下內(nèi)容的輸出結(jié)果:

    $ python3 test.py Test set accuracy: 0.958991 Classification report: precision recall f1-score support 0 0.98 0.94 0.96 474 1 0.94 0.98 0.96 477 avg / total 0.96 0.96 0.96 951 Confusion matrix: [[446 28] [ 11 466]]

    測試集的預(yù)測精確度接近96%——與預(yù)期一樣,略低于訓(xùn)練集的精確度,但也已經(jīng)相當(dāng)接近。這意味著我們的訓(xùn)練已經(jīng)獲得成功,且我們也證明了這套分類器能夠有效處理其從未見過的數(shù)據(jù)。其當(dāng)然還不夠完美——每25次嘗試中即有1次屬于分類錯誤,但對于本教程來說這一結(jié)果已經(jīng)完全令人滿意。

    分類報告與混淆矩陣顯示了與錯誤預(yù)測相關(guān)的示例統(tǒng)計信息。通過混淆矩陣,我們可以看到共有446項(xiàng)得到正確預(yù)測的女聲示例,而另外28項(xiàng)女聲示例則被錯誤地判斷為男聲。在466項(xiàng)男聲示例中分類器給出了正確結(jié)論,但有11項(xiàng)則被錯誤判斷為女聲。

    這樣看來,我們的分類器似乎不太擅長分辨女性的語音,因?yàn)槠渑曌R別錯誤率更高。分類報告/回調(diào)數(shù)字亦給出了相同的結(jié)論。

    在iOS上使用TensorFlow

    現(xiàn)在我們已經(jīng)擁有了一套經(jīng)過訓(xùn)練的模型,其擁有比較理想的測試集預(yù)測精確度。下面我們將構(gòu)建一款簡單的iOS應(yīng)用,并利用這套模型在其中實(shí)現(xiàn)預(yù)測能力。首先,我們利用TensorFlow C++庫構(gòu)建一款應(yīng)用。在下一章節(jié)中,我們將把模型引入Metal以進(jìn)行比較。

    這里我們既有好消息也有壞消息。壞消息是大家需要利用源代碼自行構(gòu)建TensorFlow。事實(shí)上,情況相當(dāng)糟糕:大家需要安裝Java方可實(shí)現(xiàn)這項(xiàng)目標(biāo)。而好消息是整個流程其實(shí)并不復(fù)雜。感興趣的朋友可以點(diǎn)擊此處查看完整指南,但以下步驟也基本能夠幫助大家解決問題(在TensorFlow 1.0上實(shí)測有效)。

    這里需要注意的是,大家應(yīng)當(dāng)安裝Xcode 8,并確保活動開發(fā)者目錄指向您Xcode的安裝位置(如果大家先安裝了Homebrew,隨后才安裝Xcode,則其可能指向錯誤位置,意味著TensorFlow將無法完成構(gòu)建):

    sudo xcode-select -s /Applications/Xcode.app/Contents/Developer

    TensorFlow利用一款名為bazel的工具進(jìn)行構(gòu)建,bazel則要求配合Java JDK 8。大家可以利用Homebrew輕松安裝必要的軟件包:

    brew cask install java brew install bazel brew install automake brew install libtool

    在完成之后,大家需要克隆TensorFlow GitHub庫。需要注意的是:請確保您指定的路徑不具備充足的空間,否則bazel會拒絕進(jìn)行構(gòu)建(沒錯,這是真的!)。我直接將其克隆到了自己的主目錄當(dāng)中:

    cd /Users/matthijs git clone https://github.com/tensorflow/tensorflow -b r1.0

    其中的-b r1.0標(biāo)記告知git克隆r1.0分支。大家可以隨意使用其它更新的分支,或者選擇使用master分支。

    備注:在MacOS Sierra上,接下來即將運(yùn)行的configure腳本會提示多項(xiàng)錯誤。為了解決問題,我不得不選擇克隆master分支。在OS X El Capitan上,使用r1.0分支則不會引發(fā)任何錯誤。

    在代碼庫克隆完成后,大家需要運(yùn)行configure腳本。

    cd tensorflow ./configure

    其會提出幾個問題,以下為我給出的回答:

    Please specify the location of python. [Default is /usr/bin/python]:????????

    我的回答是/usr/local/bin/python3,因?yàn)槲蚁M褂肞ython 3.6配合TensorFlow。如果大家選擇默認(rèn)選項(xiàng),則TensorFlow將利用Python 2.7進(jìn)行構(gòu)建。

    Please specify optimization flags to use during compilation [Default is? -march=native]:?

    在這里直接按下回車鍵,接下來的幾個問題則全部按n選擇否。

    在其問及要使用哪套Python庫時,按下回車以選擇默認(rèn)選項(xiàng)(即Python 3.6庫)。

    接下來的問題全部按n選擇否。現(xiàn)在該腳本將下載幾項(xiàng)依賴性項(xiàng)目并為構(gòu)建TensorFlow做好準(zhǔn)備。

    構(gòu)建靜態(tài)庫

    我們可以通過以下兩種方式構(gòu)建TensorFlow:

  • 在Mac系統(tǒng)上,使用bazel構(gòu)建工具。在iOS上使用Makefile。
  • 由于我們需要面向iOS進(jìn)行構(gòu)建,因此選擇選項(xiàng)二。然而,我們還需要構(gòu)建其它一些工具,因此也得涉及選項(xiàng)一的內(nèi)容。
  • 在tensorflow目錄下執(zhí)行以下腳本:

    tensorflow/contrib/makefile/build_all_ios.sh

    其會首先下載一些依賴性選項(xiàng),而后開始進(jìn)行構(gòu)建流程。如果一切順利,其將創(chuàng)建出三套接入應(yīng)用所必需的靜態(tài)庫,分別為: libtensorflow-core.a、libprotobuf.a、libprotobuf-lite.a。

    警告:構(gòu)建這些庫需要一段時間——我的iMac需要25分鐘,機(jī)型較舊的MacBook Pro則需要3個小時,而且整個過程中風(fēng)扇一直在全力運(yùn)轉(zhuǎn)!大家可能會在過程中看到一些編譯器警告甚至錯誤提示信息一閑而過。當(dāng)作沒看見就好,時間一到工作自然就緒!

    到這里工作還沒結(jié)束。我們還需要構(gòu)建其它兩款helper工具。在終端當(dāng)中運(yùn)行以下兩條命令:

    bazel build tensorflow/python/tools:freeze_graph bazel build tensorflow/python/tools:optimize_for_inference

    注意:這一過程大約需要20分鐘左右,因?yàn)槠湫枰俅螐牧汩_始構(gòu)建TensorFLow(這一次使用bazel)。

    備注:如果大家在過程中遇到了問題,請點(diǎn)擊此處參閱官方指南。

    為Mac設(shè)備構(gòu)建TensorFlow

    這部分內(nèi)容為可選項(xiàng)目,但由于大家已經(jīng)安裝了全部必要軟件包,因此為Mac系統(tǒng)構(gòu)建TensorFlow并不困難。其會創(chuàng)建一個新的pip軟件包,大家可進(jìn)行安裝以取代官方TensorFlow軟件包。

    為什么不使用官方軟件包?因?yàn)檫@樣我們才能創(chuàng)建一套包含自定義選項(xiàng)的TensorFlow版本。舉例來說,如果大家在運(yùn)行train.py腳本時遇到了“此TensorFlow庫無法利用SSE4.1指令進(jìn)行編譯”的警告提示,則可編譯一套特殊的TensorFLow版本以啟用這些指令。

    要為Mac系統(tǒng)構(gòu)建TensorFlow,請在終端中運(yùn)行以下命令:

    bazel build --copt=-march=native -c opt //tensorflow/tools/pip_package:build_pip_package? bazel-bin/tensorflow/tools/pip_package/build_pip_package /tmp/tensorflow_pkg

    其中的-march=native選項(xiàng)用于在您的CPU能夠支持的前提下,添加對SSE、AVX、AVX2以及FMA等的支持。

    隨后安裝該軟件包:

    pip3 uninstall tensorflow sudo -H pip3 install /tmp/tensorflow_pkg/tensorflow-1.0.0-XXXXXX.whl

    欲了解更多細(xì)節(jié)信息,請點(diǎn)擊此處參閱TensorFlow官方網(wǎng)站。

    “凍結(jié)”計算圖

    我們將要創(chuàng)建的iOS應(yīng)用將利用Python腳本加載之前訓(xùn)練完成的模型,并利用其作出一系列預(yù)測。

    大家應(yīng)該還記得,train.py將計算圖定義保存在了/tmp/voice/graph.pb文件當(dāng)中。遺憾的是,大家無法直接將該計算圖加載至iOS應(yīng)用當(dāng)中。完整的計算圖中包含的操作目前還不受TensorFlow C++ API的支持。正因?yàn)槿绱?#xff0c;我們才需要使用剛剛構(gòu)建完成的其它兩款工具。其中freeze_graph負(fù)責(zé)獲取graph.pb以及包含有W與b訓(xùn)練結(jié)果值的checkpoint文件。其還會移除一切在iOS之上不受支持的操作。

    在終端當(dāng)中立足tensorflow目錄運(yùn)行該工具:

    bazel-bin/tensorflow/python/tools/freeze_graph \ --input_graph=/tmp/voice/graph.pb --input_checkpoint=/tmp/voice/model \ --output_node_names=model/y_pred,inference/inference --input_binary \ --output_graph=/tmp/voice/frozen.pb

    以上命令將在/tmp/voice/frozen.pb當(dāng)中創(chuàng)建一套經(jīng)過簡化的計算圖,其僅具備y_pred與inference兩個節(jié)點(diǎn)。其并不包含任何用于訓(xùn)練的計算圖節(jié)點(diǎn)。

    使用freeze_graph的好處在于,其還將固定該文件中的權(quán)重值,這樣大家就無需分別進(jìn)行權(quán)重值加載了:frozen.pb中已經(jīng)包含我們需要的一切。而optimize_for_inference工具則負(fù)責(zé)對計算圖進(jìn)行進(jìn)一步簡化。其將作為grozen.pb文件的輸入內(nèi)容,并寫入/tmp/voice/inference.pb作為輸出結(jié)果。我們隨后會將此文件嵌入至iOS應(yīng)用當(dāng)中。使用以下命令運(yùn)行該工具:

    bazel-bin/tensorflow/python/tools/optimize_for_inference \ --input=/tmp/voice/frozen.pb --output=/tmp/voice/inference.pb\ --input_names=inputs/x --output_names=model/y_pred,inference/inference\ --frozen_graph=True

    iOS應(yīng)用

    大家可以在github.com/hollance/TensorFlow-iOS-Example中的VoiceTensorFlow文件夾內(nèi)找到我們此次使用的iOS應(yīng)用。

    在Xcode當(dāng)中打開該項(xiàng)目,其中包含以下幾條注意事項(xiàng):

  • 此應(yīng)用利用Objective-C++編寫(其源文件擴(kuò)展名為.mm)。在編寫時尚不存在面向TensorFlow的Swift API,因此只能使用C++。
  • 其中的inference.pb文件已經(jīng)包含在項(xiàng)目當(dāng)中。如果需要,大家也可以直接將自有版本的inference.pb復(fù)制到此項(xiàng)目的文件夾之內(nèi)。
  • 此應(yīng)用與Accelerate.framework相鏈接。
  • 此應(yīng)用與我們此前已經(jīng)編譯完成的幾套靜態(tài)庫相鏈接。
  • 前往Project Settings(項(xiàng)目設(shè)置)屏幕并切換至Build Settings(構(gòu)建設(shè)置)標(biāo)簽。在Other Linker Flags(其它鏈接標(biāo)記)下,大家會看到以下內(nèi)容:

    /Users/matthijs/tensorflow/tensorflow/contrib/makefile/gen/protobuf_ios/lib/ libprotobuf-lite.a /Users/matthijs/tensorflow/tensorflow/contrib/makefile/gen/protobuf_ios/lib/ libprotobuf.a -force_load /Users/matthijs/tensorflow/tensorflow/contrib/makefile/gen/lib/ libtensorflow-core.a

    除非您的名稱同樣為“matthijs”,否則大家需要將其替換為您TensorFlow庫的實(shí)際克隆路徑。(請注意,這里tensorflow出現(xiàn)了兩次,所以文件夾名稱應(yīng)為tensorflow/tensorflow/...)

    備注:大家也可以將這三個.a文件復(fù)制到項(xiàng)目文件夾之內(nèi),如此即不必?fù)?dān)心路徑可能出現(xiàn)問題。我個人并不打算在這一示例項(xiàng)目中采取這種方式,因?yàn)閘ibtensorflow-core.a文件是一套體積達(dá)440 MB的庫。

    另外請注意檢查Header Search Paths(標(biāo)題搜索路徑)。以下為目前的設(shè)置:

    ~/tensorflow ~/tensorflow/tensorflow/contrib/makefile/downloads ~/tensorflow/tensorflow/contrib/makefile/downloads/eigen ~/tensorflow/tensorflow/contrib/makefile/downloads/protobuf/src ~/tensorflow/tensorflow/contrib/makefile/gen/proto

    另外,大家還需要將其更新至您的克隆目錄當(dāng)中。

    以下為我在構(gòu)建設(shè)置當(dāng)中進(jìn)行了修改的其它條目:

    Enable Bitcode: No Warnings / Documentation Comments: No Warnings / Deprecated Functions: No

    Bitcode目前尚不受TensorFLow的支持,所以我決定將其禁用。我還關(guān)閉了警告選項(xiàng),否則在編譯應(yīng)用時會出現(xiàn)一大票問題提示。(禁用之后,大家仍然會遇到幾項(xiàng)關(guān)于值轉(zhuǎn)換問題的警告。大家當(dāng)然也可以將其一并禁用,但我個人還是希望多少了解一點(diǎn)其中的錯誤。)

    在完成了對Other Linker Flags與Header Search Paths的變更之后,大家即可構(gòu)建并運(yùn)行我們的iOS應(yīng)用。

    很好,現(xiàn)在大家已經(jīng)擁有了一款能夠使用TensorFlow的iOS應(yīng)用了!下面讓我們看看它的實(shí)際運(yùn)行效果。

    使用 TensorFlow C++ API

    TensorFlow for iOS由C++編寫而成,但其中需要編寫的C++代碼量其實(shí)——幸運(yùn)的是——并不多。一般來講,大家只需要完成以下工作:

  • 從.pb文件中加載計算圖與權(quán)重值。
  • 利用此計算圖創(chuàng)建一項(xiàng)會話。
  • 將您的數(shù)據(jù)放置在一個輸入張量內(nèi)。
  • 在一個或者多個節(jié)點(diǎn)上運(yùn)行計算圖。
  • 從輸出結(jié)果張量中獲取結(jié)果。
  • 在本示例應(yīng)用當(dāng)中,這一切皆發(fā)生在ViewController.mm之內(nèi)。首先,我們加載計算圖:

    - (BOOL)loadGraphFromPath:(NSString *)path { auto status = ReadBinaryProto(tensorflow::Env::Default(), path.fileSystemRepresentation, &graph); if (!status.ok()) { NSLog(@"Error reading graph: %s", status.error_message().c_str()); return NO; } return YES; }

    此Xcode項(xiàng)目當(dāng)中已經(jīng)包含我們通過在graph.pb上運(yùn)行freeze_graph與optimize_for_inference工具所構(gòu)建的inference.pb計算圖。如果大家希望直接加載graph.pb,則會得到以下錯誤信息:

    Error adding graph to session: No OpKernel was registered to support Op? 'L2Loss' with these attrs.? Registered devices: [CPU], Registered kernels:?? <no registered kernels>????? [[Node: loss-function/L2Loss = L2Loss[T=DT_FLOAT](model/W/read)]]

    這是因?yàn)镃++ API所能支持的操作要遠(yuǎn)少于Python API。這里提到我們在loss函數(shù)節(jié)點(diǎn)中所使用的L2Loss操作在iOS上并不適用。正因?yàn)槿绱?#xff0c;我們才需要利用freeze_graph以簡化自己的計算圖。

    在計算圖加載完成之后,我們使用以下命令創(chuàng)建一項(xiàng)會話:

    - (BOOL)createSession {tensorflow::SessionOptions options;auto status = tensorflow::NewSession(options, &session);if (!status.ok()) {NSLog(@"Error creating session: %s", status.error_message().c_str());return NO;}status = session->Create(graph);if (!status.ok()) {NSLog(@"Error adding graph to session: %s", status.error_message().c_str());return NO;}return YES; }

    會話創(chuàng)建完成后,我們可以利用其執(zhí)行預(yù)測操作。其中的predict:method會提供一個包含20項(xiàng)浮點(diǎn)數(shù)值的數(shù)組——即聲學(xué)特征——并將這些數(shù)字饋送至計算得意洋洋發(fā)中。

    下面我們一起來看此方法的工作方式:

    - (void)predict:(float *)example {tensorflow::Tensor x(tensorflow::DT_FLOAT, tensorflow::TensorShape({ 1, 20 }));auto input = x.tensor<float, 2>();for (int i = 0; i < 20; ++i) {input(0, i) = example[i];}

    其首先將張量x定義為我們需要使用的輸入數(shù)據(jù)。此張量為{1,20},因?yàn)槠湟淮翁崛∫豁?xiàng)示例且該示例中包含20項(xiàng)特征。在此之后,我們將數(shù)據(jù)由float *數(shù)組復(fù)制至該張量當(dāng)中。

    接下來,我們運(yùn)行該項(xiàng)會話:

    std::vector<std::pair<std::string, tensorflow::Tensor>> inputs = {{"inputs/x-input", x}};std::vector<std::string> nodes = {{"model/y_pred"},{"inference/inference"}};std::vector<tensorflow::Tensor> outputs;auto status = session->Run(inputs, nodes, {}, &outputs);if (!status.ok()) {NSLog(@"Error running model: %s", status.error_message().c_str());return;}

    這里得出了類似于Python代碼的內(nèi)容:

    ??? pred, inf = sess.run([y_pred, inference], feed_dict={x: example})

    只是不那么簡潔。我們需要創(chuàng)建饋送詞典、用于列出需要運(yùn)行的全部節(jié)點(diǎn)的向量,外加一個負(fù)責(zé)容納對應(yīng)結(jié)果的向量。最后,我們告知該會話完成上述任務(wù)。

    在會話運(yùn)行了全部必要節(jié)點(diǎn)后,我們即可輸出以下結(jié)果:

    auto y_pred = outputs[0].tensor<float, 2>();NSLog(@"Probability spoken by a male: %f%%", y_pred(0, 0));auto isMale = outputs[1].tensor<float, 2>();if (isMale(0, 0)) {NSLog(@"Prediction: male");} else {NSLog(@"Prediction: female");} }

    出于演示需求,只需要運(yùn)行inference節(jié)點(diǎn)即可完成對音頻數(shù)據(jù)的男聲/女聲判斷。不過我還希望查看計算得出的概率,因此這里我也運(yùn)行了y_pred節(jié)點(diǎn)。

    運(yùn)行iOS應(yīng)用

    大家可以在iPhone模擬器或者實(shí)機(jī)之上運(yùn)行這款應(yīng)用。在模擬器上,大家仍然會看到“此TensorFlow庫無法利用SSE4.1指令進(jìn)行編譯”的提示,但在實(shí)機(jī)上則不會出現(xiàn)這樣的問題。

    出于測試的目的,這款應(yīng)用只會進(jìn)行兩項(xiàng)預(yù)測:一次為男聲示例預(yù)測,一次為女聲示例預(yù)測。(我直接從測試集中提取了對應(yīng)示例。大家也可以配合其它示例并修改maleExample或者emaleExample數(shù)組當(dāng)中的數(shù)字。)

    運(yùn)行這款應(yīng)用,大家應(yīng)該會看到以下輸出結(jié)果。該應(yīng)用首先給出了計算圖當(dāng)中的各節(jié)點(diǎn):

    Node count: 9 Node 0: Placeholder 'inputs/x-input' Node 1: Const 'model/W' Node 2: Const 'model/b' Node 3: MatMul 'model/MatMul' Node 4: Add 'model/add' Node 5: Sigmoid 'model/y_pred' Node 6: Const 'inference/Greater/y' Node 7: Greater 'inference/Greater' Node 8: Cast 'inference/inference'

    需要注意的是,此計算圖中僅包含實(shí)施預(yù)測所必需的操作,而不包括任何與訓(xùn)練相關(guān)的內(nèi)容。

    此后,其會輸出預(yù)測結(jié)果:

    Probability spoken by a male: 0.970405% Prediction: male? Probability spoken by a male: 0.005632% Prediction: female

    如果大家利用Python腳本嘗試使用同樣的示例,那么結(jié)果也將完全一致。任務(wù)完成!

    備注:這里要提醒大家,此項(xiàng)演示項(xiàng)目中我們對數(shù)據(jù)進(jìn)行了“偽造”(即使用了提取自測試集中的示例)。如果大家希望利用這套模型處理真正的音頻,則首先需要將對應(yīng)音頻轉(zhuǎn)化為20項(xiàng)聲學(xué)特征。

    iOS平臺上TensorFlow的優(yōu)勢與缺點(diǎn)

    TensorFlow是一款出色的機(jī)器學(xué)習(xí)模型訓(xùn)練工具,特別是對于那些不畏數(shù)學(xué)計算并樂于創(chuàng)建新型算法的朋友。要對規(guī)模更大的模型進(jìn)行訓(xùn)練,大家甚至可以在云環(huán)境下使用TensorFLow。

    除了訓(xùn)練之外,本篇博文還介紹了如何將TensorFLow添加至您的iOS應(yīng)用當(dāng)中。對于這一部分,我希望概括這種作法的優(yōu)勢與缺點(diǎn)。

    在iOS之上使用TensorFlow的優(yōu)勢:

  • 使用一款工具即可實(shí)現(xiàn)全部預(yù)期。大家可以同時利用TensorFlow訓(xùn)練模型并將其引用于設(shè)備之上。我們不再需要將自己的計算圖移植至BNNS或者M(jìn)etal等其它API處。在另一方面,大家則必須至少將部分Python代碼“移植”為C++形式。
  • TensorFlow擁有眾多超越BNNS或Metal的出色功能特性。
  • 大家可以在模擬器上對其進(jìn)行測試。(Metal要求用戶始終利用實(shí)機(jī)進(jìn)行測試。)
  • 在iOS上使用TensorFLow的缺點(diǎn):

  • 目前其尚不支持GPU。TensorFlow確實(shí)能夠利用Acclerate框架以發(fā)揮CPU的向量指令優(yōu)勢,但在原始處理速度上仍無法與Metal相提并論。
  • TensorFLow API為C++,因此大家需要使用Objective-C++自行編寫代碼。
  • 大家無法直接利用Swift使用TensorFLow。C++ API相較于Python API存在更多局限性。這意味著大家無法在設(shè)備之上進(jìn)行數(shù)據(jù)訓(xùn)練,因?yàn)榉聪騻鞑ニ枰淖詣犹荻扔嬎闵胁皇茉O(shè)備支持。但這并不是什么大問題,畢竟移動設(shè)備的硬件本身就不適合進(jìn)行大規(guī)模數(shù)據(jù)集訓(xùn)練。
  • TensorFlow靜態(tài)庫的加入會令應(yīng)用體積增加約40 MB。大家可以通過減少受支持操作的數(shù)量對其進(jìn)行瘦身,但具體過程相當(dāng)麻煩。另外這還不包含您模型本體的體積,這可能會讓應(yīng)用尺寸進(jìn)一步膨脹。
  • 就個人來講,我認(rèn)為在iOS上使用TensorFlow并沒有什么性價比可言——至少就目前而言是如此。其優(yōu)勢根本無法抵消致命的缺點(diǎn)。不過作為一款年輕的產(chǎn)品,我相信TensorFLow未來會得到進(jìn)一步改善……

    備注:如果大家決定在自己的iOS應(yīng)用當(dāng)中使用TensorFlow,則應(yīng)意識到人們完全可以直接從應(yīng)用包中復(fù)制計算圖的.pb文件以竊取您的模型。雖然這個問題不僅存在于TensorFlow當(dāng)中,但由于“凍結(jié)”計算圖文件中同時包含模型參數(shù)與計算圖定義,因此對方能夠輕松完成逆向工程。如果您的模型將作為應(yīng)用本身的核心競爭優(yōu)勢存在,那么請務(wù)必想辦法對其加以保護(hù)以避免受到惡意窺探。

    在GPU上運(yùn)行:使用Metal

    在iOS之上使用TensorFLow的一大缺點(diǎn)在于,其運(yùn)行在CPU之上。雖然對于數(shù)據(jù)與模型規(guī)模較小的TensorFlow項(xiàng)目而言,CPU的處理能力已經(jīng)完全足夠,但對于較大的模型、特別是深度學(xué)習(xí)項(xiàng)目而言,大家無疑需要利用GPU進(jìn)行相關(guān)運(yùn)算。而在iOS系統(tǒng)上,這意味著我們必須選擇Metal。

    大家仍然需要在自己的Mac設(shè)備上利用TensorFlow進(jìn)行訓(xùn)練——或者使用其它擁有強(qiáng)大GPU的Linux設(shè)備甚至云資源——不過運(yùn)行在iOS上的引用代碼則可使用Metal而非TensorFlow庫。

    在對必需的學(xué)習(xí)參數(shù)進(jìn)行訓(xùn)練之后——即W與b值——我們需要將其導(dǎo)出為Metal可以讀取的格式。幸運(yùn)的是,我們只需要將其作為二進(jìn)制格式保存為一份浮點(diǎn)數(shù)值列表即可。

    現(xiàn)在我們需要編寫另一套Python腳本:export_weights.py(點(diǎn)擊此處查看完整版本)。其內(nèi)容與我們之前用于加載計算圖定義及checkpoint文件的test.py非常相似。不過這一次,我們使用以下內(nèi)容:

    ??? W.eval().tofile("W.bin")???? b.eval().tofile("b.bin")

    W.eval()負(fù)責(zé)計算W的當(dāng)前值并將其返回為一個NumPy數(shù)組(過程與執(zhí)行sess.run(W)完全一致)。此后,我們使用tofile()將該NumPy數(shù)據(jù)保存為一個二進(jìn)制文件。好了,就是這么簡單:-)

    備注:對于我們的示例分類器,W是一個20 x 1的矩陣,即一份簡單的20項(xiàng)浮點(diǎn)數(shù)值列表。對于更為復(fù)雜的模型,大家的學(xué)習(xí)參數(shù)可能屬于四維張量。在這種情況下,大家可能需要對其中的部分維度進(jìn)行順序調(diào)整,因?yàn)門ensorFlow存儲數(shù)據(jù)的順序與Metal的預(yù)期存在差異。大家可以直接使用tf.transpose()命令實(shí)現(xiàn)這一目標(biāo),但再次重申,我們的這一示例項(xiàng)目并不需要這些過程。

    下面來看我們這套邏輯分類器的Metal版本。大家可以點(diǎn)擊此處在其源代碼的VoiceMetal文件夾中找到對應(yīng)的Xcode項(xiàng)目。此項(xiàng)目以Swift語言編寫而成。

    大家應(yīng)該還記得,這里的邏輯回歸算法采用了以下方程式進(jìn)行計算:

    y_pred = sigmoid((W * x) + b)

    其計算過程與神經(jīng)網(wǎng)絡(luò)當(dāng)中完全連接層的執(zhí)行過程相同。因此為了利用Metal實(shí)現(xiàn)我們的分類器,只需要使用一個MPSCNNFullyConnected層。首先,我們將W.bin與b.bin加載至Data對象當(dāng)中:

    let W_url = Bundle.main.url(forResource: "W", withExtension: "bin" let b_url = Bundle.main.url(forResource: "b", withExtension: "bin" let W_data = try! Data(contentsOf: W_url!) let b_data = try! Data(contentsOf: b_url!)

    此后,我們創(chuàng)建該完全連接層:

    let sigmoid = MPSCNNNeuronSigmoid(device: device) let layerDesc = MPSCNNConvolutionDescriptor(kernelWidth: 1, kernelHeight: 1, inputFeatureChannels: 20, outputFeatureChannels: 1, neuronFilter: sigmoid)W_data.withUnsafeBytes { W inb_data.withUnsafeBytes { b inlayer = MPSCNNFullyConnected(device: device, convolutionDescriptor: layerDesc, kernelWeights: W, biasTerms: b, flags: .none)} }

    由于輸入內(nèi)容為20個數(shù)字,我決定將完全連接層的設(shè)定為一套1 x 1且包含20條輸入通道的維度“圖像”。而結(jié)果y_pred僅為單一數(shù)字,這樣該完全連接層將僅擁有一條輸出通道。作為輸入與輸出數(shù)據(jù)駐留所在的對象,MPSImage同樣擁有這些維度:

    let inputImgDesc = MPSImageDescriptor(channelFormat: .float16, width: 1, height: 1, featureChannels: 20)let outputImgDesc = MPSImageDescriptor(channelFormat: .float16, width: 1, height: 1, featureChannels: 1)inputImage = MPSImage(device: device, imageDescriptor: inputImgDesc) outputImage = MPSImage(device: device, imageDescriptor: outputImgDesc)

    由于使用的是應(yīng)用中的TensorFlow版本,因此其中的predict方法將獲取用以構(gòu)建單一示例的20條浮點(diǎn)數(shù)值。以下為完整的方法內(nèi)容:

    func predict(example: [Float]) {convert(example: example, to: inputImage)let commandBuffer = commandQueue.makeCommandBuffer()layer.encode(commandBuffer: commandBuffer, sourceImage: inputImage, destinationImage: outputImage)commandBuffer.commit()commandBuffer.waitUntilCompleted()let y_pred = outputImage.toFloatArray()print("Probability spoken by a male: \(y_pred[0])%")if y_pred[0] > 0.5 {print("Prediction: male")} else {print("Prediction: female")} }

    這即為Metal當(dāng)中的運(yùn)行會話版本。其中convert(example:to:)與toFloatArray()方法屬于helper,負(fù)責(zé)將數(shù)據(jù)加載進(jìn)/出MPSImage對象。就是這么簡單,我們已經(jīng)成功完成了Metal版本的應(yīng)用成果!大家需要在實(shí)機(jī)之上運(yùn)行此應(yīng)用,因?yàn)镸etal并不支持模擬器運(yùn)行機(jī)制。

    Probability spoken by a male: 0.970215% Prediction: male? Probability spoken by a male: 0.00568771% Prediction: female

    需要注意的是,這些概率與TensorFlow提供的預(yù)測結(jié)果并不完全一致。這是因?yàn)镸etal會在內(nèi)部使用16位浮點(diǎn)數(shù)值,但二者的結(jié)果仍然相當(dāng)接近!

    鳴謝

    本示例當(dāng)中使用的數(shù)據(jù)集由Kory Becker構(gòu)建并下載自Kaggle.com。感興趣的朋友亦可參閱Kory的博文與源代碼。

    其他作者亦發(fā)布了與在iOS系統(tǒng)之上使用TensorFlow相關(guān)的文章,我本人也從中得到了大量啟發(fā)并引用了部分代碼示例,具體包括:

    《在iOS上使用Deep MNIST與TensorFlow:上手指南》,由 Matt Rajca撰寫。

    《利用Metal Performance Shaders實(shí)現(xiàn)TensorFlow加速》,同樣來自 Matt Rajca。《Tensorflow Cocoa示例》,由Aaron Hillegass撰寫。

    TensorFlow資源庫中的《TensorFlow iOS示例》。

    原文鏈接:http://machinethink.net/blog/tensorflow-on-ios/

    總結(jié)

    以上是生活随笔為你收集整理的深度学习指南:在iOS平台上使用TensorFlow的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

    国产精品久久久久av福利动漫 | 摸阴视频 | 91精品在线看 | 美女视频是黄的免费观看 | 日韩91av | 国产xx视频| 久久男人免费视频 | 久久伊人91| 色午夜影院 | 区一区二区三区中文字幕 | 天天操天天干天天爽 | 午夜999| 久草在线视频中文 | 麻豆91视频 | 麻豆极品 | 亚洲2019精品| 中文字幕丝袜制服 | 狠狠综合久久 | 天天天天爽| av一级二级 | 中文字幕网址 | 黄色在线观看网站 | 久久久久女人精品毛片九一 | 久久综合九色综合97_ 久久久 | 国产高清在线免费视频 | 国产 日韩 中文字幕 | 日韩av成人免费看 | 黄色小说在线观看视频 | 日韩av女优视频 | 美女av在线免费 | 91久久国产综合精品女同国语 | 韩国av电影在线观看 | 国产高清精 | 久热超碰| av国产在线观看 | 搡bbbb搡bbb视频 | 日韩电影一区二区在线 | 国产麻豆精品在线观看 | 亚洲 欧洲 国产 精品 | 99久久久久免费精品国产 | 九九热精品视频在线观看 | 欧美日韩在线观看不卡 | 色吊丝在线永久观看最新版本 | 99热精品国产 | 97人人精品| 久久视频免费在线观看 | 一区二区欧美激情 | 免费在线观看不卡av | 五月开心婷婷 | 97免费在线视频 | 国产精品99久久久久 | 草莓视频在线观看免费观看 | 美女黄色网在线播放 | 日韩欧美视频免费观看 | 九九热只有这里有精品 | 丁香婷婷色综合亚洲电影 | 国产在线国偷精品产拍免费yy | 久久这里只有精品1 | 黄色片免费电影 | 成人蜜桃网 | 91免费观看视频网站 | 97香蕉超级碰碰久久免费软件 | 亚洲成人精品av | 夜夜夜影院 | 亚洲一级在线观看 | 91在线视频在线 | 91丨九色丨国产丨porny精品 | 欧美激情综合色 | 久久久久成 | 麻豆免费观看视频 | 日韩免费观看视频 | 欧美日韩午夜爽爽 | 精品免费99久久 | 日韩av伦理片 | 欧美热久久 | 亚洲 欧美日韩 国产 中文 | 欧美91视频 | 97超碰人人澡人人爱 | 日韩精品在线一区 | www.成人sex | 国产一卡二卡在线 | 日本在线中文 | 国产成人在线免费观看 | 亚洲在线免费视频 | 五月天国产精品 | 伊人超碰在线 | 国产特级毛片aaaaaaa高清 | 日韩欧美在线影院 | 国产精品久久久久一区二区三区共 | 成人在线免费观看网站 | 午夜视频免费在线观看 | 国产精成人品免费观看 | 五月天婷亚洲天综合网鲁鲁鲁 | 亚洲精品国偷拍自产在线观看蜜桃 | 国产精品69久久久久 | 亚洲国产中文字幕 | 国产精品久久久久久久久久久久午夜 | 叶爱av在线 | 美女久久99 | 亚洲激精日韩激精欧美精品 | 91一区二区三区久久久久国产乱 | 日韩美av在线 | 久久视频免费观看 | 伊人国产女 | 亚洲国产精品99久久久久久久久 | 国产理论一区二区三区 | www..com黄色片| 婷婷综合亚洲 | 女人18毛片a级毛片一区二区 | 天天曰天天射 | 99精品欧美一区二区蜜桃免费 | 国产91免费观看 | 97视频播放| 综合伊人av | 国产精品成人免费一区久久羞羞 | 中文字幕免费观看全部电影 | 亚洲成人av一区 | 国产高清不卡在线 | 丁香婷婷在线观看 | 九九久久精品视频 | av在线中文 | 亚洲好视频 | 夜色资源站wwwcom | 中文字幕在线不卡国产视频 | 亚洲最大av在线播放 | 美女网站黄在线观看 | 18国产精品福利片久久婷 | 久久夜视频| 香蕉国产91 | 特级毛片在线免费观看 | 久久免费视频在线观看 | 96精品高清视频在线观看软件特色 | 日韩免费在线观看视频 | av福利免费 | 国产女人18毛片水真多18精品 | 国产手机在线 | 欧美日韩在线免费视频 | 69久久夜色精品国产69 | 国产精品一区免费观看 | 日批视频在线观看免费 | 日韩精品免费在线视频 | 久久久午夜精品福利内容 | 又色又爽又黄 | 激情av在线资源 | www蜜桃视频 | 在线免费黄色av | 国产亚洲午夜高清国产拍精品 | 精品国产自在精品国产精野外直播 | 最近2019中文免费高清视频观看www99 | 久久久久久久久久免费视频 | 日韩久久激情 | 91视频a | 天堂黄色片 | 韩国精品福利一区二区三区 | 久久99精品波多结衣一区 | 国产做爰视频 | 欧美激情视频一二区 | 欧美视频在线二区 | 狠狠色丁香久久综合网 | 天天艹天天干天天 | 特黄免费av | 五月激情av | 日本最新一区二区三区 | 99视频| 久久理论影院 | 婷婷丁香花五月天 | 99精品国产在热久久 | 美女免费视频一区 | 国产精品麻豆视频 | 九九热在线观看视频 | 97碰在线| 国产乱对白刺激视频不卡 | 天天综合网入口 | 91精品国产91久久久久久三级 | 日本一区二区高清不卡 | 九九免费在线观看视频 | a级国产乱理论片在线观看 伊人宗合网 | 在线观看av大片 | 成人在线视频你懂的 | 国产午夜在线观看 | 亚洲成av人影片在线观看 | 国产精品系列在线播放 | 久久久精品网站 | 97狠狠操| 色五丁香| 久热只有精品 | 色婷婷久久久综合中文字幕 | 国产精品久久人 | 亚洲精品国产综合久久 | 99精品欧美一区二区三区 | 久久精品国产精品亚洲 | 久久久激情视频 | 国产亚洲高清视频 | a视频在线观看 | 中文字幕黄色av | 国产一级在线免费观看 | 在线观看免费福利 | 久久免费视频播放 | www.久久成人 | 天天综合网久久综合网 | 日韩视频免费看 | 天天操天天干天天爱 | 天天干一干 | 免费看黄视频 | 婷婷丁香在线 | av看片在线 | 久草在线播放视频 | 亚洲国产资源 | 国产在线日本 | 在线观看香蕉视频 | 久久婷婷国产色一区二区三区 | 久久精品国产一区二区 | 国产专区在线 | 欧美日韩不卡在线 | 在线播放 日韩专区 | 中文字幕色在线视频 | 亚洲3级 | 在线香蕉视频 | 超碰在线观看99 | 久草视频免费在线观看 | 在线观看第一页 | 91桃花视频| 在线视频1卡二卡三卡 | 五月婷婷开心中文字幕 | 日本免费久久高清视频 | 国产不卡毛片 | 亚洲艳情| 日韩理论电影网 | 在线观看免费中文字幕 | 狠狠躁天天躁综合网 | 深爱五月激情网 | 国产美女精品视频免费观看 | 国产xxxx| 免费av看片| 九九免费在线观看视频 | 一区二区毛片 | 国产不卡av在线播放 | 最近免费中文字幕mv在线视频3 | 成年人免费在线播放 | 国产精品高潮呻吟久久av无 | 国产91粉嫩白浆在线观看 | 精品自拍网 | 六月久久婷婷 | 国产日产在线观看 | 色综合夜色一区 | 欧美日韩国产亚洲乱码字幕 | 欧美一级电影在线观看 | 天天爱天天射 | 92国产精品久久久久首页 | 午夜久久久久 | 综合在线观看 | 涩涩色亚洲一区 | 中文字幕在线播放一区二区 | 992tv在线观看 | 午夜av在线 | 久久久国产一区二区三区 | 色偷偷男人的天堂av | .精品久久久麻豆国产精品 亚洲va欧美 | 天天插天天射 | 中文字幕国产精品 | 狠狠狠狠干 | 超碰最新网址 | 91日韩在线视频 | 521色香蕉网站在线观看 | 一区二区三区四区不卡 | 丁香六月久久综合狠狠色 | 97天天干| 国产精品久久久久婷婷 | 亚洲精品国产综合久久 | 一区二区伦理 | 狠狠网站 | 在线韩国电影免费观影完整版 | 国产99久久久久久免费看 | 在线视频第一页 | 久久九九久久 | 欧美天堂影院 | 日韩av一区二区在线播放 | 狠狠干五月天 | 亚洲一区av | 久草免费在线视频观看 | 综合久久久 | 911亚洲精品第一 | 草在线视频 | av在线网站免费观看 | 91在线小视频 | 国产日产亚洲精华av | 99久久精品日本一区二区免费 | 天天射天天操天天色 | 国产中文字幕国产 | 久久久久草| 日本中文字幕网址 | 日韩在线第一区 | 丁香电影小说免费视频观看 | 激情视频91 | 日本乱码在线 | 成人a免费 | 午夜精品电影 | 久久精彩视频 | 国产精品免费视频久久久 | 中文字幕123区 | 日韩av免费大片 | 成人av播放 | 97成人免费视频 | 九九热在线精品 | 久久精品一区二区三区四区 | 久久99精品国产91久久来源 | 久久福利综合 | 999成人免费视频 | 国产高清在线视频 | 中文字幕有码在线观看 | 欧美久久九九 | 在线播放 日韩专区 | 超碰在线最新网址 | 国产精品免费久久久久久久久久中文 | 中文字幕一区二区三区在线视频 | 成人小视频在线观看免费 | 91av手机在线观看 | 免费看短 | 91精品久久久久久久久久入口 | 国产一区二区三区在线 | 一本一本久久a久久 | 一级黄色大片在线观看 | 天天添夜夜操 | 日日躁天天躁 | 久草在线视频免赞 | 在线欧美国产 | 精品国产一区二区三区男人吃奶 | 一区二区视频播放 | 国产精品久久亚洲 | 亚洲精品网址在线观看 | 国产精品久久久久久久久软件 | 四虎影视成人永久免费观看视频 | 在线91精品 | 免费看的国产视频网站 | av片无限看 | 精品福利片 | 国产精品1区2区3区 久久免费视频7 | 国产在线小视频 | 日韩一区二区三区免费视频 | 婷婷色网视频在线播放 | 国产成人精品综合 | 精品v亚洲v欧美v高清v | www91在线观看| 97视频在线播放 | 欧美日韩亚洲在线观看 | 久久国产精品免费 | 久久久精品福利视频 | 激情五月播播久久久精品 | 操处女逼 | 亚洲精品午夜一区人人爽 | 狠狠操狠狠 | 日韩精品一区二 | 久久网站免费 | 91麻豆国产福利在线观看 | 夜夜婷婷| 一性一交视频 | 91免费在线视频 | 国产一级特黄毛片在线毛片 | 最新免费中文字幕 | 91完整版在线观看 | 久久成人精品电影 | 国产精品久久久久av | 国语精品免费视频 | 中文乱幕日产无线码1区 | 日韩成人在线一区二区 | 成人av高清在线 | 九九免费在线观看视频 | 国产一区二区视频在线 | 久热只有精品 | 日韩理论片中文字幕 | 在线看一级片 | 9在线观看免费高清完整版在线观看明 | 国产精品麻豆三级一区视频 | 天天操天天摸天天爽 | 亚洲天天干 | 国内久久精品视频 | 西西大胆免费视频 | 久久热首页 | 国产一区二区三精品久久久无广告 | 国产原创av在线 | 国产婷婷视频在线 | av官网| 国产免费视频一区二区裸体 | 97在线视频网站 | 激情五月播播久久久精品 | 国产剧情一区二区在线观看 | 欧美午夜精品久久久久久浪潮 | 亚洲欧美视频一区二区三区 | 欧美一区日韩一区 | 国产精品美 | 色资源网免费观看视频 | 亚洲在线精品视频 | 国产在线视频在线观看 | 亚洲成人家庭影院 | 99 视频 高清 | 色综合久久88色综合天天免费 | 免费黄色激情视频 | 中文字幕不卡在线88 | 日韩专区av | ww视频在线观看 | 亚洲精品午夜一区人人爽 | 久久极品 | 欧美日韩色婷婷 | 欧美巨乳波霸 | 91精品国产一区二区三区 | 中文字幕在线观看av | 国产日韩欧美在线影视 | 天天天干 | 成年人三级网站 | 国产免费一区二区三区最新6 | 天天射综合网站 | 在线日韩亚洲 | 久久久国产一区二区三区 | 免费a级黄色毛片 | 黄色av一区二区 | 国产手机av在线 | 亚洲电影成人 | 亚洲视频精品在线 | 中文字幕乱偷在线 | 成人a视频片观看免费 | 久久久精品 一区二区三区 国产99视频在线观看 | 欧美精彩视频在线观看 | 成人在线观看网址 | av解说在线观看 | 久久有精品 | 69av国产 | www.狠狠插.com | 亚洲毛片一区二区三区 | 九色激情网| 性色大片在线观看 | 午夜三级大片 | 天堂av在线中文在线 | 丁香婷婷色 | 日韩系列在线 | 久草亚洲视频 | 亚洲成人av片在线观看 | 999视频网| 国产色久 | 在线观看av免费 | 最近高清中文字幕 | 六月丁香婷婷在线 | 精品在线你懂的 | 精品国产一区二区三区噜噜噜 | 国产视频2021| 久久精品一二三 | 毛片基地黄久久久久久天堂 | 狠狠色丁香婷婷综合久久片 | 亚洲黄色免费网站 | 夜色成人网 | 又污又黄网站 | 免费色av | 黄色在线观看www | 91成人精品一区在线播放69 | 一区二区三区高清在线观看 | 蜜桃视频成人在线观看 | 天天操天天干天天爱 | 一区二区视频在线播放 | 在线国产能看的 | 久久婷婷国产色一区二区三区 | 人人澡超碰碰97碰碰碰软件 | av网站播放| www毛片com| 国产精品原创视频 | 国产精品午夜久久 | 精品美女视频 | 日韩欧美国产成人 | 精品国产一区在线观看 | 欧美日韩在线观看一区 | 6080yy精品一区二区三区 | 91精品对白一区国产伦 | 操高跟美女| 亚洲黄色激情小说 | www.天天干 | 国产二区免费视频 | 精品九九九 | 一区二区三区视频网站 | 久操中文字幕在线观看 | 日韩免费电影 | 在线一区电影 | 激情电影在线观看 | 91精品国产一区二区三区 | 精品欧美小视频在线观看 | 91av网站在线观看 | 国产在线观看免 | 国产精品 国内视频 | 三级黄色在线 | 天天插日日插 | 久久99久久99精品 | 91在线免费视频观看 | 99热国产精品 | 91高清一区| 久草网在线视频 | 91精品啪在线观看国产81旧版 | 六月激情网 | 97品白浆高清久久久久久 | 深夜免费小视频 | 99久久99久久精品国产片 | 成人在线免费小视频 | 激情综合网色播五月 | 免费观看一区二区三区视频 | 超碰成人av | 久久久久成 | 最近日本mv字幕免费观看 | 久久手机精品视频 | 久久午夜免费观看 | 91爱爱中文字幕 | 国产成人精品久久久久 | 久久久久久久久网站 | 国产九九热视频 | 中文亚洲欧美日韩 | 精品亚洲在线 | 日韩理论在线播放 | 国产黄色片久久久 | 色综合人人 | 黄色aaa级片 | 99久久精品国产欧美主题曲 | 九九免费观看视频 | 欧美aaa一级 | 欧美日韩一区二区三区在线观看视频 | 国产夫妻性生活自拍 | 日本免费一二三区 | 婷婷av网 | 日韩精品一区二区在线视频 | 91久久一区二区 | 韩国av三级 | 欧美精品久久久久久久亚洲调教 | 又黄又爽免费视频 | 五月天丁香视频 | 日韩欧美在线视频一区二区 | 天天干天天射天天插 | 日韩黄色中文字幕 | 成人av免费在线观看 | 国产成人一区二区三区在线观看 | 国产精品久久久久久久免费观看 | 一级电影免费在线观看 | 国产精品久久久久久久久久久不卡 | 国产精品免费观看视频 | 日韩免费中文字幕 | 99久久这里有精品 | 国产日韩欧美在线观看 | 久久精品国产精品亚洲精品 | 777视频在线观看 | av丝袜在线 | 草在线| 国产高清成人 | 婷婷在线免费观看 | 日韩性片 | 亚洲精品免费看 | 久久黄色精品视频 | 国产精品97| 久久免费精彩视频 | zzijzzij日本成熟少妇 | 色国产精品一区在线观看 | 在线激情av电影 | 亚洲一区黄色 | 在线电影日韩 | 亚洲视频播放 | 国产破处在线视频 | 亚洲精品乱码久久久久久蜜桃91 | 亚洲综合色丁香婷婷六月图片 | www.夜夜干.com | 亚洲成av人影片在线观看 | 女人久久久久 | 丁香六月婷婷激情 | 狠狠色丁香婷婷综合视频 | 国产91对白在线 | 午夜精品久久久久久久久久久久久久 | 网站免费黄色 | 久草精品视频 | 色噜噜日韩精品一区二区三区视频 | 二区三区av | 99精品在线直播 | 久久久首页 | 亚洲经典中文字幕 | 精品久久久影院 | 国产999视频 | 久久精品99国产精品亚洲最刺激 | 欧美精品一区二区三区一线天视频 | 国产黄网在线 | avv天堂| 天堂中文在线视频 | 91av亚洲 | 日本最大色倩网站www | 欧美9999 | 精品99在线 | 在线观看免费成人av | 91精品国自产在线偷拍蜜桃 | 不卡电影免费在线播放一区 | 久久久亚洲网站 | 亚洲一区二区三区精品在线观看 | 久久优 | 久草精品网 | 亚洲国产一二三 | 伊人在线视频 | av国产在线观看 | 色偷偷网站视频 | 三级av片 | 久久成| 欧美a在线免费观看 | 日韩啪视频 | 干av在线| 丁香亚洲| 丁香激情五月婷婷 | 久久免费精品视频 | 大型av综合网站 | 中文字幕韩在线第一页 | 五月激情久久 | 嫩草av影院| 夜夜躁天天躁很躁波 | 国产精品乱码久久久久 | 日本精品视频在线 | 97超碰站| 欧美91精品久久久久国产性生爱 | 日韩在线视频精品 | 亚洲精品999 | 在线看的av网站 | 五月激情丁香 | 手机av电影在线观看 | 人人爽人人爽人人爽人人爽 | 色 免费观看 | 国产免费观看高清完整版 | 久久久久久久久久电影 | 免费麻豆网站 | 国产精品资源在线 | 激情影音| 欧美在线观看小视频 | 欧美成年网站 | 伊人婷婷| 精品麻豆入口免费 | 精品国产一区二区三区久久久 | 97av影院| 91爱爱网址 | 深夜免费福利视频 | 香蕉成人在线视频 | 91精品视频观看 | 国产精品不卡在线 | 日韩电影中文,亚洲精品乱码 | av不卡免费看 | 日韩有码在线观看视频 | 色综合久久五月天 | 国产在线a不卡 | 精品久久久99 | 久产久精国产品 | 亚洲精品免费观看 | 天天操天天谢 | 日本深夜福利视频 | 九七人人干 | 色综合久久久久综合 | 男女免费视频观看 | 久久久久久蜜桃一区二区 | 一区二区国产精品 | 国产一级一片免费播放放a 一区二区三区国产欧美 | 免费久久网站 | 亚洲免费观看在线视频 | 啪啪肉肉污av国网站 | 成人国产在线 | 欧美日韩国产三级 | 999久久久免费精品国产 | 国产在线精品视频 | 国产精品乱码一区二三区 | 久草在线在线 | 日日操操 | 亚洲欧美精品一区 | 国产69久久久 | 色婷婷在线视频 | 成年人视频在线免费播放 | 免费高清在线一区 | 国产综合在线观看视频 | 久久综合久久综合这里只有精品 | 成人毛片100免费观看 | 综合久久2023 | 亚洲免费av在线 | 亚洲在线资源 | 日韩精品专区在线影院重磅 | 久久久久久电影 | 人人射人人射 | 久久不射电影院 | 99一级片 | 国产视频精品久久 | www.99av | 国产123区在线观看 国产精品麻豆91 | 日本黄色免费在线 | 久久久综合香蕉尹人综合网 | 91精品视频在线免费观看 | 丁香久久综合 | 91久久精品日日躁夜夜躁国产 | 激情伊人五月天 | 久久久影视 | 91伊人影院 | 免费aa大片| 欧美日韩中文国产一区发布 | 久久99国产精品免费网站 | 欧美日韩国产精品爽爽 | 亚洲国产99 | 日韩在线一区二区免费 | 免费观看性生交 | 亚洲在线免费视频 | 91麻豆看国产在线紧急地址 | 久久精品永久免费 | 五月天综合激情 | 色婷婷九月| www国产在线 | 日韩电影在线观看中文字幕 | 久久全国免费视频 | 日韩69av| 在线香蕉视频 | 久久综合色综合88 | 最近2019好看的中文字幕免费 | 一本一本久久a久久精品牛牛影视 | www.色五月.com | 色av色av色av| 欧美成人久久 | 久久精彩免费视频 | 观看免费av| 国产日本高清 | 天天在线视频色 | 久久超碰网 | 伊人视频 | 五月婷婷操 | 99精品国产一区二区三区不卡 | 天天操比 | 亚洲尺码电影av久久 | 色婷婷免费 | 精品超碰 | 天堂av在线网| 欧美在线一二区 | 亚洲精品国产精品国自 | 精品国产一区二区三区在线 | 久久国内免费视频 | 国产精品久久久久久久婷婷 | 在线观看免费91 | 超碰日韩在线 | av超碰免费在线 | 成人av一区二区三区 | 99久久久久久久久 | 手机看片午夜 | 黄网站色成年免费观看 | 91成人网在线观看 | 97人人模人人爽人人喊网 | 美女久久久久久久久久 | 天堂av免费 | 成年人视频在线免费观看 | 黄色av电影在线 | 在线午夜 | 波多野结衣网址 | 日韩午夜电影网 | 日韩一区二区三免费高清在线观看 | 91视频高清 | 免费男女网站 | 国产在线精品一区二区不卡了 | 久久不射电影院 | 五月婷婷av在线 | 中文字幕在线观 | 成人免费看电影 | 亚洲黄在线观看 | 婷婷激情网站 | www久久| 国产69精品久久久久9999apgf | 国产免费一区二区三区最新 | 久久高清 | 精品黄色在线 | 日韩美女免费线视频 | 成在人线av | 一区二区三区免费 | 在线视频你懂得 | 国产精品一级在线 | 国产高清在线精品 | 一区二区三区在线免费观看 | 视频99爱 | 精品视频在线观看 | 精精国产xxxx视频在线播放 | 99在线免费观看 | 97小视频| 五月的婷婷 | 国产精品久久久影视 | 欧美国产精品久久久久久免费 | 日韩激情视频在线 | 最新中文在线视频 | 日本在线成人 | 日韩 | 日日碰狠狠躁久久躁综合网 | 亚洲一级理论片 | 免费看成人片 | 国产不卡在线播放 | 999视频在线播放 | 国产亚洲精品成人av久久影院 | 欧美日韩69 | 亚洲欧美日韩在线看 | 国产精品欧美一区二区 | 久久96国产精品久久99软件 | 在线免费高清一区二区三区 | 久久综合干 | 91亚洲网 | 亚洲在线高清 | 欧美黄在线 | 91精品天码美女少妇 | 最新av中文字幕 | 97精品久久人人爽人人爽 | 日韩精品久久久久久中文字幕8 | 色婷婷视频 | 一区二区三区www | www.伊人网| 国产黄色成人av | 欧美日韩一区二区在线 | 成年人免费看av | 成人av电影在线观看 | 99久久精品费精品 | 久久综合中文字幕 | 九九热免费观看 | 国产一区二区三区在线免费观看 | 成人免费 在线播放 | 日韩久久视频 | 精品久久99 | 日日天天 | 永久免费精品视频 | 天天操夜夜逼 | 久久人人爽视频 | 欧美国产不卡 | 日韩激情在线 | 91麻豆精品国产91久久久无需广告 | 国产 日韩 中文字幕 | 久久国产精品99久久久久久进口 | ,午夜性刺激免费看视频 | 亚洲人精品午夜 | 精品久久久久_ | 中文字幕视频一区 | 日日干天天爽 | 久久免费电影网 | 久久婷婷五月综合色丁香 | 黄免费网站 | 九九爱免费视频在线观看 | 国产91精品欧美 | 亚洲精品乱码久久久久久高潮 | 欧美日韩精品在线 | 中文字幕有码在线播放 | 国产亚洲精品久久久久久大师 | 国产成人精品一区二区三区福利 | 伊人网综合在线观看 | 伊人婷婷综合 | 亚洲国产日韩一区 | 91精品国产欧美一区二区 | 亚洲精品久久久久久久不卡四虎 | 天天曰天天射 | 久久久色| 国产人成看黄久久久久久久久 | 日韩av午夜在线观看 | 国精产品999国精产品视频 | 亚洲精品综合久久 | 日韩乱色精品一区二区 | 黄色资源网站 | 日韩欧美电影在线观看 | 国产偷国产偷亚洲清高 | 精品在线播放 | 日韩欧美在线播放 | 久久久综合电影 | www黄在线| 91日韩精品一区 | 在线视频 国产 日韩 | 97视频在线播放 | 午夜精品久久久99热福利 | 一区二区视频免费在线观看 | 午夜精品久久久久久久99婷婷 | 国产精品日韩欧美一区二区 | 青青河边草免费观看完整版高清 | 免费人成网ww44kk44 | 久久综合一本 | 日日干天天爽 | 欧美在线观看视频一区二区三区 | 久久伊人爱 | 毛片基地黄久久久久久天堂 | 四虎国产精品成人免费4hu | 国产日韩精品一区二区 | 精品久久久久久综合日本 | 亚洲综合小说电影qvod | 亚洲黄色av| 亚洲在线黄色 | 四虎精品成人免费网站 | 97av视频| av一区二区在线观看中文字幕 | 最近的中文字幕大全免费版 | 久久久九九 | 国产色网站 | 日本久久成人中文字幕电影 | 国产成人一区二区三区久久精品 | 久久久成人精品 | 成人免费在线观看入口 | 婷婷av资源| 亚洲精品视频免费观看 | 一区二区伦理电影 | 亚洲精品视频一二三 | 免费成人看片 | 精品免费观看 | 日韩视频一区二区三区在线播放免费观看 | 在线观看色网站 | 国产a国产| 免费观看十分钟 | 开心综合网 | 国产99在线免费 | 久久情爱 | 国产成人黄色片 | www178ccom视频在线 | 天天要夜夜操 | 久久精品日韩 | 成人av动漫在线 | 91香蕉视频在线下载 | 亚洲黄色高清 | 久久精品久久综合 | 97超碰国产精品女人人人爽 | 日韩动态视频 | 人人爽夜夜爽 | 国产不卡av在线 | 国产精品成人久久久久 | av在线播放中文字幕 | 午夜av在线| 欧洲精品久久久久毛片完整版 | 国产精品美女视频网站 | 精品国产91亚洲一区二区三区www | 久久久久久久99 | 一区二区激情视频 | 992tv在线观看 | 色久综合 | 午夜少妇一区二区三区 | 欧美日韩国产亚洲乱码字幕 | 国产精品久久久久久久免费 | 91av在线电影 | 在线播放 日韩专区 | 久久精品8 | 六月色丁香 | 久久久久激情电影 | 丁香九月激情综合 | 超碰97中文 | 中文字幕在线免费看 | 婷婷视频在线观看 | 日本天天操 | 五月婷av| 亚洲视频 在线观看 | 最近高清中文在线字幕在线观看 | 国产特级毛片aaaaaaa高清 | 日韩视频免费在线 | 视频在线观看91 | 欧美在线一级片 | 久久视频精品 | 日韩欧美在线不卡 | 国产探花在线看 | 亚洲视频一区二区三区在线观看 | 狠狠色丁香婷综合久久 | 在线观看成人国产 | 91xav| 又色又爽又黄 | 国产99久久久久久免费看 | 国产黄色精品在线观看 | 日韩av一区二区三区 | 麻豆视频免费看 | 一区在线观看视频 | www.av免费 | 九精品 | 久草视频在线播放 | 91麻豆文化传媒在线观看 | 国产高清久久久久 | 六月丁香激情综合 | 亚洲精选在线观看 | 婷婷伊人网| 免费黄色特级片 | 国产在线精品观看 | av成人免费观看 | 一本一道波多野毛片中文在线 | 亚洲作爱视频 | 日日操日日操 | 色婷婷av在线 | 国产精品一区在线观看你懂的 | 福利视频导航网址 | 国产专区一| 日本二区三区在线 | 久久午夜精品影院一区 | 日本黄色免费在线观看 | 日韩视频免费 | 欧美日本高清视频 | 亚洲资源 | 亚洲国产日韩av | 日韩高清一二区 | 日韩在线一区二区免费 | 欧美国产日韩一区 | 天天av资源| 久久高视频 | 国产精品高清在线观看 | 91精品免费在线视频 | 亚洲精品乱码久久久久久高潮 | 久久综合爱| 玖玖在线观看视频 | 成人久久视频 | 日韩在线观看高清 | 黄色免费电影网站 | 婷婷丁香视频 | 黄网站大全 | 天天天综合 | 国产精品av久久久久久无 | 久久免费高清 | 欧美视频二区 |