在利用深度学习网络进行预测性分析之前,我们首先需要对其加以训练。目前市面上存在着大量能够用于神经网络训练的工具,但TensorFlow无疑是其中极为重要的首选方案之一。
\\大家可以利用TensorFlow训练自己的机器学习模型,并利用这些模型完成预测性分析。训练通常由一台极为强大的设备或者云端资源完成,但您可能想象不到的是,TensorFlow亦可以在iOS之上顺利起效——只是存在一定局限性。
\\在今天的博文中,我们将共同了解TensorFlow背后的设计思路、如何利用其训练一套简单的分类器,以及如何将上述成果引入您的iOS应用。
\\在本示例中,我们将使用“根据语音与对话分析判断性别”数据集以了解如何根据音频记录判断语音为男声抑或女声。
\\获取相关代码:大家可以通过GitHub上的对应项目获取本示例的源代码。
\\TensorFlow是什么,我们为何需要加以使用?
\\TensorFlow是一套用于构建计算性图形,从而实现机器学习的软件资源库。
\\其它一些工具往往作用于更高级别的抽象层级。以Caffe为例,大家需要将不同类型的“层”进行彼此互连,从而设计出一套神经网络。而iOS平台上的BNNS与MPSCNN亦可实现类似的功能。
\\在TensorFlow当中,大家亦可处理这些层,但具体处理深度将更为深入——甚至直达您算法中的各项计算流程。
\\大家可以将TensorFlow视为一套用于实现新型机器学习算法的工具集,而其它深度学习工具则用于帮助用户使用这些算法。
\\当然,这并不是说用户需要在TensorFlow当中从零开始构建一切。TensorFlow拥有一整套可复用的构建组件,同时囊括了Keras等负责为TensorFlow用户提供大量便捷模块的资源库。
\\因此TensorFlow在使用当中并不强制要求大家精通相关数学专业知识,当然如果各位愿意自行构建,TensorFlow也能够提供相应的工具。
\\利用逻辑回归实现二元分类
\\在今天的博文当中,我们将利用逻辑回归(logistic regression)算法创建一套分类器。没错,我们将从零开始进行构建,因此请大家做好准备——这可是项有点复杂的任务。所谓分类器,其基本工作原理是获取输入数据,而后告知用户该数据所归属的类别——或者种类。在本项目当中,我们只设定两个种类:男声与女声——也就是说,我们需要构建的是一套二元分类器(binary classifier)。
\\备注:二元分类器属于最简单的一种分类器,但其基本概念与设计思路同用于区分成百上千种不同类别的分类器完全一致。因此,尽管我们在本份教程中不会太过深入,但相信大家仍然能够从中一窥分类器设计的门径。
\\在输入数据方面,我们将使用包含20个数字朗读语音、囊括多种声学特性的给定录音。我将在后文中对此进行详尽解释,包括音频频率及其它相关信息。
\\在以下示意图当中,大家可以看到这20个数字全部接入一个名为sum的小框。这些连接拥有不同的weights(权重),对于分类器而言代表着这20个数字各自不同的重要程度。
\\以下框图展示了这套逻辑分类器的起效原理:
\\在sum框当中,输入数据区间为x0到x19,且其对应连接的权重w0到w19进行直接相加。以下为一项常见的点积:
\\\sum = x[0]*w[0] + x[1]*w[1] + x[2]*w[2] + ... + x[19]*w[19] + b\\
我们还在所谓bias(偏离)项的末尾加上了b。其仅仅代表另一个数字。
\\数组w中的权重与值b代表着此分类器所学习到的经验。对该分类器进行训练的过程,实际上是为了帮助其找到与w及b正确匹配的数字。最初,我们将首先将全部w与b设置为0。在数轮训练之后,w与b则将包含一组数字,分类器将利用这些数字将输入语音中的男声与女声区分开来。为了能够将sum转化为一条概率值——其取值在0与1之间——我们在这里使用logistic sigmoid函数:
\\\y_pred = 1 / (1 + exp(-sum))\\
这条方程式看起来很可怕,但做法却非常简单:如果sum是一个较大正数,则sigmoid函数返回1或者概率为100%; 如果sum是一个较大负数,则sigmoid函数返回0。因此对于较大的正或者负数,我们即可得出较为肯定的“是”或者“否”预测结论。
\\然而,如果sum趋近于0,则sigmoid函数会给出一个接近于50%的概率,因为其无法确定预测结果。当我们最初对分类器进行训练时,其初始预期结果会因分类器本身训练尚不充分而显示为50%,即对判断结果并无信心。但随着训练工作的深入,其给出的概率开始更趋近于1及0,即分类器对于结果更为肯定。
\\现在y_pred中包含的预测结果显示,该语音为男声的可能性更高。如果其概率高于0.5(或者50%),则我们认为语音为男声; 相反则为女声。
\\这即是我们这套利用逻辑回归实现的二元分类器的基本设计原理。输入至该分类器的数据为一段对20个数字进行朗读的音频记录,我们会计算出一条权重sum并应用sigmoid函数,而我们获得的输出概率指示朗读者应为男性。
\\然而,我们仍然需要建立用于训练该分类器的机制,而这时就需要请出今天的主角——TensorFlow了。
\\在TensorFlow中实现此分类器
\\要在TensorFlow当中使用此分类器,我们需要首先将其设计转化为一套计算图(computational graph)。一项计算图由多个负责执行计算的节点组成,且输入数据会在各节点之间往来流通。
\\我们这套逻辑回归算法的计算图如下所示:
\\看起来与之前给出的示意图存在一定区别,但这主要是由于此处的输入内容x不再是20个独立的数字,而是一个包含有20个元素的向量。在这里,权重由矩阵W表示。因此,之前得出的点积也在这里被替换成了一项矩阵乘法。
\\另外,本示意图中还包含一项输入内容y。其用于对分类器进行训练并验证其运行效果。我们在这里使用的数据集为一套包含3168条example语音记录的集合,其中每条示例记录皆被明确标记为男声或女声。这些已知男声或女声结果亦被称为该数据集的label(标签),并作为我们交付至y的输入内容。
\\为了训练我们的分类器,这里需要将一条示例加载至x当中并允许该计算图进行预测:即语音到底为男声抑或是女声?由于初始权重值全部为0,因此该分类器很可能给出错误的预测。我们需要一种方法以计算其错误的“具体程度”,而这一目标需要通过loss函数实现。Loss函数会将预测结果y_pred与正确输出结果y进行比较。
\\在将loss函数提供给训练示例后,我们利用一项被称为backpropagation(反向传播)的技术通过该计算图进行回溯,旨在根据正确方向对W与b的权重进行小幅调整。如果预测结果为男声但实际结果为女声,则权重值即会稍微进行上调或者下调,从而在下一次面对同样的输入内容时增加将其判断为“女声”的概率。
\\这一训练规程会利用该数据集中的全部示例进行不断重复再重复,直到计算图本身已经获得了最优权重集合。而负责衡量预测结果错误程度的loss函数则因此随时间推移而变低。
\\反向传播在计算图的训练当中扮演着极为重要的角色,但我们还需要加入一点数学手段让结果更为准确。而这也正是TensorFlow的专长所在:我们只要将全部“前进”操作表达为计算图当中的节点,其即可自动意识到“后退”操作代表的是反向传播——我们完全无需亲自进行任何数学运算。太棒了!
\\Tensorflow到底是什么?
\\在以上计算图当中,数据流向为从左至右,即代表由输入到输出。而这正是TensorFlow中“流(flow)”的由来。不过Tensor又是什么?
\\Tensor一词本义为张量,而此计算图中全部数据流皆以张量形式存在。所谓张量,其实际代表的就是一个n维数组。我曾经提到W是一项权重矩阵,但从TensorFlow的角度来看,其实际上属于一项二阶张量——换言之,一个二组数组。
\\一个标量代表一个零阶张量。
\\- 一个向量代表一个一阶张量。\\t
- 一个矩阵代表一个二阶张量。\\t
- 一个三维数组代表一个三阶张量。\\t
- 之后以此类推……\
这就是Tensor的全部含义。在卷积神经网络等深度学习方案当中,大家会需要与四维张量打交道。但本示例中提到的逻辑分类器要更为简单,因此我们在这里最多只涉及到二阶张量——即矩阵。
\\我之前还提到过,x代表一个向量——或者说一个一阶张量——但接下来我们同样将其视为一个矩阵。y亦采用这样的处理方式。如此一来,我们即可将数据库组视为整体对其loss进行计算。
\\一条简单的示例(example)语音内包含20个数据元素。如果大家将全部3168条示例加载至x当中,则x会成为一个3168 x 20的矩阵。再将x与W相乘,则得出的结果y_pred为一个3168 x 1的矩阵。具体来讲,y_pred代表的是为数据集中的每条语音示例提供一项预测结论。
\\通过将我们的计算图以矩阵/张量的形式进行表达,我们可以一次性对多个示例进行预测。
\\安装TensorFlow
\\好的,以上是本次教程的理论基础,接下来进入实际操作阶段。
\\我们将通过Python使用TensorFlow。大家的Mac设备可能已经安装有某一Python版本,但其版本可能较为陈旧。在本教程中,我使用的是Python 3.6,因此大家最好也能安装同一版本。
\\安装Python 3.6非常简单,大家只需要使用Homebrew软件包管理器即可。如果大家还没有安装homebrew,请点击此处参阅相关指南。
\\接下来打开终端并输入以下命令,以安装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级数组协作的库。听起来耳熟吗?NumPy并非将其称为张量,但之前提到了数组本身就是一种张量。TensorFlow Python API就建立在NumPy基础之上。\\
- SciPy是一套用于数值计算的库。其它一些软件包的起效需要以之为基础。\\t
- pandas负责数据集的加载与清理工作。\\t
- scikit-learn在某种意义上可以算作TensorFlow的竞争对手,因为其同样是一套用于机器学习的库。我们之所以在本项目中加以使用,是因为它具备多项便利的功能。由于TensorFlow与scikit-learn皆使用NumPy数组,因为二者能够顺畅实现协作。\
实际上,大家无需pandas与scikit-learn也能够使用TensorFlow,但二者确实能够提供便捷功能,而且每一位数据科学家也都乐于加以使用。
\\如大家所知,这些软件包将被安装在/usr/local/lib/python3.6/site-packages当中。如果大家需要查看部分未被公布在其官方网站当中的TensorFlow源代码,则可以在这里找到。
\\备注:pip应会为您的系统自动安装TensorFlow的最佳版本。如果大家希望安装其它版本,则请点击此处参阅官方安全指南。另外,大家也可以利用源代码自行构建TensorFlow,这一点我们稍后会在面向iOS构建TensorFlow部分中进行说明。
\\下面我们进行一项快速测试,旨在确保一切要素都已经安装就绪。利用以下内容创建一个新的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))\\
而后通过终端运行这套脚本:
\\\python3 tryit.py\\
其会显示一些与TensorFlow运行所在设备相关的调试信息(大多为CPU信息,但如果您所使用的Mac设备配备有英伟达GPU,亦可能提供GPU信息)。最终结果显示为:
\\\\\[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.\\
如果出现上述内容,则代表您在系统当中安装的TensorFlow并非当前CPU的最优适配版本。修复方法之一是利用源代码自行构建TensorFlow,因为这允许大家对全部选项加以配置。但在本示例当中,由于其不会造成什么影响,因此直接忽略即可。
\\深入观察训练数据集
\\要训练分类器,我们自然需要数据。
\\在本项目当中,我们使用来自Kory Becker的“根据语音判断性别”数据集。为了能够让这份教程能够与TensorFlow指南上的MNIST数字化识别有所不同,这里我决定在Kaggle.com上寻找数据集,并最终选定了这一套。
\\那么我们到底该如何立足音频实现性别判断?下载该数据集并打开voice.csv文件之后,大家会看到其中包含着一排排数字:
\\我们首先需要强调这一点,这里列出的并非实际音频数据!相反,这些数字代表着语音记录当中的不同声学特征。这些属性或者特征由一套脚本自音频记录中提取得出,并被转化为这个CSV文件。具体提取方式并不属于本篇文章希望讨论的范畴,但如果大家感兴趣,则可点击此处查阅其原始R源代码。
\\这套数据集中包含3168项示例(每项示例在以上表格中作为一行),且基本半数为男声录制、半数为女声录制。每一项示例中存在20项声学特征,例如:
\\以kHz为单位的平均频率
\\- 频率的标准差\\t
- 频谱平坦度\\t
- 频谱熵\\t
- 峰度\\t
- 声学信号中测得的最大基频\\t
- 调制指数\\t
- 等等……\
别担心,虽然我们并不了解其中大多数条目的实际意义,但这不会影响到本次教程。我们真正需要 关心的是如何利用这些数据训练自己的分类器,从而立足于上述特征确保其有能力区分男性与女性的语音。
\\如果大家希望在一款应用程序当中使用此分类器,从而通过录音或者来自麦克风的音频信息检测语音性别,则首先需要从此类音频数据中提取声学特征。在拥有了这20个数字之后,大家即可对其分类器加以训练,并利用其判断语音内容为男声还是女声。
\\因此,我们的分类器并不会直接处理音频记录,而是处理从记录中提取到的声学特征。
\\\\\备注:我们可以以此为起点了解深度学习与逻辑回归等传统算法之间的差异。我们所训练的分类器无法学习非常复杂的内容,大家需要在预处理阶段提取更多数据特征对其进行帮助。在本示例的特定数据集当中,我们只需要考虑提取音频记录中的音频数据。
\\深度学习最酷的能力在于,大家完全可以训练一套神经网络来学习如何自行提取这些声学特征。如此一来,大家不必进行任何预处理即可利用深度学习系统采取原始音频作为输入内容,并从中提取任何其认为重要的声学特征,而后加以分类。
\\这当然也是一种有趣的深度学习探索方向,但并不属于我们今天讨论的范畴,因此也许日后我们将另开一篇文章单独介绍。
\
建立一套训练集与测试集
\\在前文当中,我提到过我们需要以如下步骤对分类器进行训练:
\\向其交付来自数据集的全部示例。
\\- 衡量预测结果的错误程度。\\t
- 根据loss调整权重。\
事实证明,我们不应利用全部数据进行训练。我们只需要其中的特定一部分数据——即测试集——从而评估分类器的实际工作效果。因此,我们将把整体数据集拆分为两大部分:训练集,用于对分类器进行训练; 测试集,用于了解该分类器的预测准确度。
\\为了将数据拆分为训练集与测试集,我创建了一套名为split_data.py的Python脚本,其内容如下所示:
\\\import numpy as np # 1 \import pandas as pd df = pd.read_csv(\"voice.csv\