全栈开发高可扩展,高可维护性当当书城WebApp网盘分享

#1

download:全栈开发高可扩展,高可维护性当当书城WebApp网盘分享

使用TensorFlow识别语音命令:前进、停止、左转、右转
一、老张的需求
我有个朋友叫老张,是做传统单片机的。他经常会做一些简单的硬件发明,比如他家窗帘的开合,都是通过自己设计的电路板来控制的。他的布线很乱,从电视柜的插座直接拉电线到阳台的窗户上,电线交错如蛛网。
老张媳妇强迫症,完美主义,受不了乱拉电线。但是,她怕影响丈夫成为大发明家。于是,她把绿萝沿着电线绑好,这样就把电线藏起来了,一点也不影响房间的美观。
上周,老张邀请我去他家参观这个电动窗帘。老张非常兴奋。他赶紧拿来一个凳子,站在上面,用拖把杆捅了捅一个红色的按钮。他激动得差点滑倒。我问他为什么你把开关放在这么远的地方。老张说是为了防止3岁的儿子频繁按开关。老张站在凳子上,试着像跳天鹅湖一样戳戳脚趾上的按钮,为我示范窗帘的开合。
我赶紧夸了他的窗帘。我不知道,这种情况下,如果他摔倒了,从法律上讲,我是连带责任的。
我赶紧转移注意力:哎,你的绿萝长得不错。我不自觉的摸了摸树叶,感觉手指都麻了。我去!老张,你的绿萝带刺了!
老张说,不是带刺,可能是带电。
我并不太惊讶,因为我认识老张已经10多年了。先是高中3年同学,然后大学4年同学,然后2年同事。在老张这里,什么怪事都有可能发生。我还记得,我高中的时候,他晚上用勺子抱着半个西瓜,上厕所蹲着,上下同步。他完全没有不好意思的意思,称之为豁达。
我回忆往事,痛苦万分。今天,它将成为过去。我起身离开。老张说,我从事嵌板多年。知道为什么一直没有起色吗?
我说,什么?电路板上还能改进吗?!
老张说,不是,我是说,我从事嵌入式工作这么多年,一直都是平平淡淡的。主要原因,我感觉是没有结合高科技,比如人工智能。而你现在从事的是人工智能。
我说,我能让你好起来吗?
老张说,可以。我所有的巡逻车都是由无线电控制的。当我按下它时,我发出无线电波。你帮我弄个声控。我大喊,快跑!它一直往前走。我喊,站住!它停止了。我大喊:往左,往右!它会转动。
我说,这个不难。但是,这有用吗?
老张说,对,这很好。据我所知,我们车间还没有人能想出我的主意。而我,另一方面,很快就能做到。
我说,是的。你并不复杂。不过,我也有一个要求。那就是我在博客里写你,让网友知道这件事,好吗?
老张说:没问题!
第二,我的研究
人工智能有三个共同的领域:视觉、书写和语音。前两个我写了很多。这一次,我开始专注于语音领域。

下面这段代码,环境要求tensor flow 2.6(2021年10月以后)+python 3.9。

2.1语音分析
我们看到和听到的都是数据。体现在电脑里,就是数字。
比如我们看到下面这张像素图,是4*4像素。图片上有两个紫色的点。你看起来像这样。

实际上,如果是黑白的单通道,数据看起来是这样的:
[[255, 255, 255, 255],
[255, 131, 255, 255],
[255, 131, 255, 255],
[255, 255, 255, 255]]
复制代码
如果是多通道,即彩色,数据如下:
[[[255, 255,255],[255, 255, 255],[255, 255, 255],[255, 255, 255]],
[[255, 255, 255],[198, 102, 145],[255, 255, 255],[255, 255, 255]],
[[255, 255, 255],[198, 102, 145],[255, 255, 255],[255, 255, 255]],
[[255, 255, 255],[255, 255, 255],[255, 255, 255],[255, 255, 255]]]
复制代码
我们看到空的都是255。只是两个紫色方块变了。彩色值为[198,102,145],单色值为131。可以说一切都是数据。
我们的耳朵听到声音。但是,事实上,它也是数据。
如果你不相信我,让我们分析一个音频文件。
#根据文件路径,解码音频文件
将张量流作为tf导入
audio _ binary = TF . io . read _ file(" datasets \ \ go \ \ 1000 . wav ")
音频,速率= tf.audio.decode_wav(内容=音频_二进制)
复制代码
Tf.audio.decode_wav可以用来读取和解码音频文件,返回数据音频和音频文件的采样率。
其中,解析数据的音频打印如下:

数组([[-0.00238037),
[-0.0038147 ],
[-0.00335693],
…,
[-0.00875854],
[-0.00198364],
[-0.00613403]],dtype=float32)>
复制代码
以上数据,以[[x]]的形式。这意味着该音频是单声道的(就像黑白照片一样)。是X通道中某一时刻的具体值。它实际上是一个波形,我们可以画出来。
将matplotlib.pyplot作为plt导入
plt.plot(音频)
plt.show()
复制代码

这个波的大小就是推动你耳膜的力量。

上图显示了11146个采样点的形状。接下来,我们打印10个点的形状。这10个点就像推你耳朵10次。
将matplotlib.pyplot作为plt导入
plt.plot(音频[0:10])
plt.show()
复制代码

至此,我们可以看到,音频其实就是几组有序列的数字。
要识别音频,首先要分析音频数据的特征。
2.2音频频谱
每个个体都有自己的组成部分,它们是独一无二的。就像你一样。
然而,许多个体之间有相似之处。就像我们都是程序员一样。所以,我们可以用一种叫做“光谱”的东西来描述一个东西。比如辣鸡的食谱。它是食谱,描述了放多少辣椒,用鸡肉的哪一部分,切成什么形状。这让我们一看到成品就大喊:辣鸡,不是糖醋鱼。
声音也有“谱”,一般用谱来描述。
声音是由振动产生的,这种振动的频率是有频谱的。

分析一个声音包含哪些固定频率就像分析一道由辣椒、鸡肉和豆酱组成的菜。通过成分分析,最终可以判断出是什么样的食物。
声音也是如此。声波可以分析它的频率组成。如果你想了解更多关于“频谱”的知识,我有一篇一万字的长文解释“最后,掘金有人讲了傅立叶变换”。需要半个小时才能完成。

我上面说的,谷歌已经知道了。所以在TensorFlow框架中,他们内置了获取音频频谱的功能。它采用短时傅里叶变换stft。
波形= tf.squeeze(音频,轴=-1)
spectrogram = tf.signal.stft(波形,帧长度=255,帧步长=128)
复制代码
我们已经通过上面的tf.audio.decode_wav解析了音频文件,其返回的数据格式为[[-0.00238037][-0.0038147 ]]。
你可能会奇怪,为什么不是[-0.00238037,-0.0038147]的形式,还要再盖一层。回想一下我们的紫色像素的例子,其中一个像素被表示为[[198,102,145]],这意味着RGB三个颜色值通道描述了一个彩色像素。其实这里也一样。这是一个多频道兼容的情况。
然而,我们只需要一段话。所以需要通过tf.squeeze(audio,axis=-1)将数据降维,将[[-0.00238037][-0.0038147 ]]改为[-0.00238037,-0.0038147 ]。这是一个纯波形。嗯,这个会交给傅立叶先生分析。
tf.signal.stft中的参数是指采样样本的规则。也就是从总波形中,每隔一段时间取多少个小样本进行分析。分析之后,我们还可以像绘制波形一样绘制出分析的频谱结果。

看不懂上图也没关系。很正常,很正常,极其正常。因为,尽管我用了一万多字,50多张图,还是做了详细的解释。但是,仍有20%左右的读者不了解。
但是,在这一点上,你需要明白,一个声音的特征是可以通过科学方法提取出来的。这就够了。
提取特征后,我们会把它们交给人工智能框架进行训练。
2.3音频数据的预处理
以上,我们成功获得了音频的一个重要灵魂:频谱。
接下来,就该交给神经网络模型进行训练了。
实际上,在正式移交给模型之前,还有一些预处理工作要做。比如把它上面的毛边全部剪掉,折叠起来,排列成同样的形状。

就像计算机只能识别0和1一样,很多框架只能接收固定的结构化数据。
举个简单的例子,你在训练古诗的时候,有五言诗和七言诗。例如,“我的床脚闪着如此明亮的光”和“一顿饥饿的饭”是两个句子。然后,最后都需要加工成同样的长度。要么前面加0,后面加0,要么把长的剪短。简而言之,必须是同样的长度。
玲玲,我的床脚闪着如此明亮的光
饭后饿。
蜀南0000
复制代码
那么,我们如何处理我们的音频数据呢?经过短时傅立叶变换后,我们的声波数据格式如下:

数组([[[4.62073803e-01],
…,
[2.92062759e-05]],

[[3.96062881e-01],
[2.01166332e-01],
[2.09505502e-02],
…,
[1.43915415e-04]]],dtype=float32)>
复制代码
这是因为我们的11146长度的音频在tf.signal.stft的frame_step=128之后可以分成86份所以我们看到shape=(86,129,1)。然后,如果音频的长度发生变化,那么这个结构也会发生变化。这可不好。
所以首先要规范音频的长度。因为采样率是16000,也就是每秒记录16000次音频数据。那么,我们不妨把1秒音频,也就是16000个长度,作为一个标准单位。如果太长,我们就把后面剪掉。如果太短,我们会在后面加0。
我说的这一系列操作,反映在代码中,如下:
波形= tf.squeeze(音频,轴=-1)
输入长度= 16000
波形=波形[:输入长度]
zero _ padding = TF . zeros([16000]-TF . shape(waveform),dtype=tf.float32)
waveform = tf.cast(waveform,dtype=tf.float32)
equal _ length = TF . concat([波形,零填充],0)
spectrogram = TF . signal . STFT(equal _ length,frame_length=255,frame_step=128)
声谱图= tf.abs(声谱图)
光谱图=光谱图[…,tf.newaxis]
复制代码
此时,让我们看看我们的频谱数据结构:

数组([[[4.62073803e-01],
…,
[2.92062759e-05]],

[0.00000000e+00],
…,
[0.000000000 e+00]]],dtype=float32)>
复制代码
现在,无论你输入多长的音频,最终它的频谱都是shape=(124,129,1)。从图中我们也可以看出,即使把不足补上0,长度也得是16000。

现在,真的是时候建立一个神经网络了。
2.4构建模型和培训
按照老张的要求…我现在不想提他,因为我的手指还在被绿萝刺痛。
根据要求…他要四个命令,分别是:前进,停止,左转,右转。然后,我做了四种音频,放在相应的文件夹下。

从文件夹中读取数据,配对输入输出,按比例划分数据集和验证集,将数据集分批次……这些操作在TensorFlow中已经非常成熟。而且随着版本的更新,越来越成熟。在代码中,字数越来越少。这里就不说了。我会把完整的代码上传到github供你参考。
接下来,我将在本例中重点介绍语音分类的实现、其神经网络的结构以及模型训练的配置。
将张量流作为tf导入
从tensorflow.keras导入图层
来自tensorflow.keras导入模型

模特=模特。顺序([
层次。输入(shape= (124,129,1)),
层次。调整大小(32,32),
层次。规范化(),
层次。Conv2D(32,3,activation='relu '),
层次。Conv2D(64,3,activation='relu '),
层次。MaxPooling2D(),
层次。辍学(0.25),
层次。Flatten(),
层次。Dense(128,激活='relu '),
层次。辍学(0.5),
层次。密集(4),
])
模型.编译(
optimizer = TF . keras . optimizer s . Adam(),
loss = TF . keras . loss . sparsecategoricalcrossentropy(from _ logits = True),
度量=[‘准确性’]
)
复制代码
其实我感觉人工智能应用层面的开发,前处理,后处理都比较难。中间的机型基本都有固定的招式。
第一层,层层叠叠。Input(shape= (124,129,1)),称为输入层,是训练样本的数据结构。这是上一节我们补了16000后,通过计算频谱得到的(124,129)的结构。
最后一层,一层层。密集(4),是输出层。我们对四个文件夹进行了分类:“Go”、“Stop”、“Left”和“Right”,最终结果是4类,所以是4。
头尾基本固定后,这个序列顺序的意思是:吃掉音频文件,然后排出它是四类中的哪一类。
然后我们可以在中间自己操作。正常化就是正常化。Conv2D是卷积。MaxPooling2D是池化。Dropout(0.25)是一个随机截掉一定比例(这里是25%)以保证其鲁棒性的神经网络。最后,通过Flatten()将多维数据展平为一维数据。给后面一个激活功能,收缩神经元数量,准备着陆。最后,连接到Dense(4)。
因此,前16,000个音频采样点被转换成某个分类。

最后,训练并保存模型。
模型=创建模型()
CP _ callback = TF . keras . callbacks . model check point(file path = ’ model/model . ckpt ',
save_weights_only=True,
save_best_only=True)
历史=模型.拟合(
火车_ds,
验证数据=val_ds,
纪元=50,
回调=[cp_callback]
)
复制代码
Filepath='model/model.ckpt '表示训练完成后的存储路径。Save_weights_only=True仅存储重量数据。Save_best_only=True表示只存储最佳训练结果。调用训练很简单,只需调用model.fit并传入训练集、验证集、训练轮数和训练回调。
2.5加载模型并预测。
在上一节中,我们指定了保存模型的路径。调用model.fit后,结果会保存在相应的路径中。这是我们想要的最终产品:

我们可以加载这些文件,这样我们的程序就有了多年的技巧。您可以预测传入的音频文件。
模型=创建模型()
如果OS . path . exists(’ model/model . ckpt . index ‘):
model . load _ weights(’ model/model . ckpt ‘)
labels = [‘go ‘,’ left ‘,’ right ‘,’ stop’]
#音频文件转码
audio = get _ audio _ data(’ my sound . wav ')
audios = NP . array([音频])
预测=模型(音频)
index = np.argmax(预测值[0])
打印(标签[索引])
复制代码
在上面的代码中,首先加载历史模型。然后对我录的一个mysound.wav文件进行预处理,方式就是前面说的采集16000,然后通过短时傅立叶变换解析成(124,129)结构的频谱数据。这是我们训练时的样子。
最后输入到模型中。出于惯性,它会输出被归类为“go”的语音命令。尽管有这种模式,但我从来没有见过我美妙的声音。但也可以认。我发出了一个声音,带有‘go’的声音特征。
以上是使用TensorFlow框架进行声音分类的全过程。