first commit

This commit is contained in:
Jingfan Ke 2024-09-05 12:45:40 +08:00
commit e148adef80
98 changed files with 2808 additions and 0 deletions

View File

@ -0,0 +1,71 @@
# 课程作业
- 课程名称:计算机语音技术
- 作业编号作业1
- 学号21281280
- 姓名:柯劲帆
- 班级物联网2101班
---
## 1. 什么是语音,语音交际过程分为哪五个阶段,各阶段的内容是什么?
语音是语言的声学实现,即传递语言信息的声学信号。
语音交际过程分为以下5个阶段
1. **语义规划**:出现发音的意图,大脑对信息处理和转化,将意图进行编码;
2. **语音产生**:大脑控制发音相关器官的运动和调节进行发音,横膈肌挤压肺部产生气流,声门、声道和唇齿震动发声;
3. **声音传递**:声音主要由四个维度携带语音信号,包括音强、音长、音高和音质;
4. **语音感知**:耳朵的复杂结构将声波的各个频率分解,并转换成电信号通过神经传递进大脑;
5. **语义理解**:大脑将接收到的语音信号转化为可以理解的思想或信息,即对语音信息进行反编码,并结合认知和记忆系统对信息进行处理和理解。
## 2. 语音的基本声学特征有哪些?请加以简要说明。
- **音质**:音质是指声音的特色和纯净度,即声音在传播过程中表现出来的泛音和震动的复杂性。语音的音质与声带、口腔、鼻腔、喉咙等发音器官的形状、大小、紧张程度等因素有关,此外还受到发音方式、发音时气流的特点、声音的共振等因素的影响。
- **音强**:音量是指声音的强度或响度,即声音在空气中的振幅。语音的音量与声带的紧张程度、呼出气体的多少以及声音在空气中传播的距离有关。一般来说,声音的响度越大,声音传播得越远。
- **音高**:音高是指声音的频率,即每秒振动声带的次数。语音的音高与声带的长短、厚薄以及松紧程度有关,一般来说,男人的音高较低,女人的音高较高。
- **音长**:音长是指声音的持续时间,即声音从开始到结束的时间长度。语音的音长与发音速度、发音时所需时间等因素有关,它可以用来表达说话人的情感和态度。
## 3. 汉语的音节结构,试举例说明各部分,以及各部分单元的个数。
在汉语中音节声母韵母声调柯KE = 声母K + 韵母E + 第1声。
汉语音节结构各部分单元的个数:
- 声母21个
- 韵母39个
- 单元音10个
- 舌面元音7个
- 舌尖元音2个
- 卷舌元音1个
- 二合元音9个
- 前响5个
- 后响4个
- 三合元音4个
- 鼻韵尾7个
- 复合鼻韵尾9个
- 声调5个
- 孤立音节声调4个
- 轻声1个
## 4. 从语音产生的过程说明辅音和元音的区别。
辅音和元音的主要区别:
1. 气流方面:
1. 辅音口腔内有阻塞或挤压,气流强烈。辅音是由于声带振动或者气流受到阻碍而产生的。在发音过程中,声带部分或全部振动,但并不像元音那样没有阻碍。气流通过口腔或鼻腔时,会被口腔或鼻腔内部的形状或紧张状态所阻碍,这种阻碍就形成了辅音。因此,辅音通常带有某种程度的摩擦、挤迫或阻碍的感觉。
2. 元音气流平稳,口腔内无明显阻碍。元音是在发音过程中,声带振动且气流不受阻碍产生的。气流通过口腔时,不遇到任何显著的阻碍。口腔的形状、大小和紧张程度都适中,使得气流能够顺畅地通过,这样的发音过程就形成了元音。因此,元音通常被认为是一种“无阻碍”的发音方式。
2. 辅音视其发音部位(如摩擦、送气、阻塞)和方式(如唇、齿、硬腭、软腭、舌面、舌根、鼻)分类;元音视声道长度、舌位和唇形分类。
## 5. 绘制语音产生模型框图,并加以简要说明。
![语音产生模型框图.drawio](语音产生模型框图.drawio.svg)

View File

@ -0,0 +1,49 @@
<mxfile host="Electron" modified="2023-09-23T16:59:42.025Z" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/21.7.5 Chrome/114.0.5735.289 Electron/25.8.1 Safari/537.36" etag="JUpnaB9MXtwt9jDfJp81" version="21.7.5" type="device">
<diagram name="第 1 页" id="srzzxO-FdIVnfBNWfj8P">
<mxGraphModel dx="810" dy="469" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="0" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="HXZTd7DcZxRO3KjASpro-16" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" edge="1" parent="1" source="HXZTd7DcZxRO3KjASpro-2" target="HXZTd7DcZxRO3KjASpro-6">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="HXZTd7DcZxRO3KjASpro-2" value="激励源" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="320" y="120" width="160" height="80" as="geometry" />
</mxCell>
<mxCell id="HXZTd7DcZxRO3KjASpro-3" value="声带振动,产生不同音高和音质的谐振源" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" vertex="1" parent="HXZTd7DcZxRO3KjASpro-2">
<mxGeometry y="30" width="160" height="50" as="geometry" />
</mxCell>
<mxCell id="HXZTd7DcZxRO3KjASpro-18" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="HXZTd7DcZxRO3KjASpro-6" target="HXZTd7DcZxRO3KjASpro-8">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="HXZTd7DcZxRO3KjASpro-6" value=" 声道调节" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="200" y="280" width="160" height="80" as="geometry" />
</mxCell>
<mxCell id="HXZTd7DcZxRO3KjASpro-7" value="辅音和元音在口腔共振腔内调节,产生共振峰" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" vertex="1" parent="HXZTd7DcZxRO3KjASpro-6">
<mxGeometry y="30" width="160" height="50" as="geometry" />
</mxCell>
<mxCell id="HXZTd7DcZxRO3KjASpro-8" value="辐射" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="200" y="400" width="160" height="80" as="geometry" />
</mxCell>
<mxCell id="HXZTd7DcZxRO3KjASpro-9" value="声音从口腔传到嘴唇形成语音信号" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" vertex="1" parent="HXZTd7DcZxRO3KjASpro-8">
<mxGeometry y="30" width="160" height="50" as="geometry" />
</mxCell>
<mxCell id="HXZTd7DcZxRO3KjASpro-14" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" edge="1" parent="1" source="HXZTd7DcZxRO3KjASpro-12" target="HXZTd7DcZxRO3KjASpro-2">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="HXZTd7DcZxRO3KjASpro-12" value="冲激序列发生器" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="320" width="160" height="80" as="geometry" />
</mxCell>
<mxCell id="HXZTd7DcZxRO3KjASpro-13" value="横膈膜有规律地挤压肺,产生序列气流" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" vertex="1" parent="HXZTd7DcZxRO3KjASpro-12">
<mxGeometry y="30" width="160" height="50" as="geometry" />
</mxCell>
<mxCell id="HXZTd7DcZxRO3KjASpro-17" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="HXZTd7DcZxRO3KjASpro-15" target="HXZTd7DcZxRO3KjASpro-6">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="HXZTd7DcZxRO3KjASpro-15" value="&lt;p style=&quot;margin-top: 10.8pt; margin-bottom: 0pt; direction: ltr; unicode-bidi: embed; vertical-align: baseline;&quot;&gt;随机噪声发生器&lt;/p&gt;" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="80" y="120" width="160" height="80" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@ -0,0 +1,178 @@
<h1><center>课程作业</center></h1>
<div style="text-align: center;">
<div><span style="display: inline-block; width: 65px; text-align: center;">课程名称</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">计算机语音技术</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">作业次数</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">第2次</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">学号</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">21281280</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">姓名</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">柯劲帆</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">班级</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">物联网2101班</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">指导老师</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">朱维彬</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">修改日期</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">2023年10月14日</span></div>
</div>
---
## 1. 问题1
**语音信号由声学信号转换成离散的数字序列要经过那些过程?模数转换的指标是什么,之前为何要加抗混叠滤波器?**
语音信号由声学信号转换成离散的数字序列要经过以下过程:
1. 拾音:麦克风将语音信号从声波转换成模拟信号;
2. 放大:电路中放大器将模拟信号放大;
3. 抗混叠滤波:基于奈奎斯特采样定理,滤波器将原始信号中高于采样频率两倍的频率成分去除,避免频率混叠;
4. 模/数转换:将模拟信号离散化,转换成离散的数字序列,其中分两步:
1. 取样:等时间间隔取样,将信号在在时间上离散化;
2. 量化:在数值上离散化,将信号幅度转换成二进制序列表示的整数。
模数转换的指标有两个:
- 采样频率:$F_{\text{sample}} > 2 \times F_n $,需要满足奈奎斯特采样定理;
- 量化精度:
- 量化字长$B$:能将最大幅度量化成$2^B$等分,其决定了能够量化的幅度范围或精度。
- 噪声$e$$e$的方差为$\sigma^2_e = \frac{1}{3}\left(\frac{\frac{2X_{\text{max}}}{2^B}}{2}\right)^2 = \frac{1}{3}\left(\frac{X_{\text{max}}}{2^B}\right)^2$,量化噪声工程估计为$\operatorname{SNR}\left(\text{dB}\right) = 6.02B-7.2$。
加抗混叠滤波器的原因:
根据奈奎斯特采样定理,如果采样频率$F_{\text{sample}}$小于两倍的最高频率成分$F_n$,那么在采样过程中,高于奈奎斯特频率的高频成分会混叠到基带频率,导致采样后的信号出现错误,如下图所示:
<img src="p1.png" alt="p1" style="zoom:33%;" />
因此需要加抗混叠滤波器,滤除信号中高于奈奎斯特采样频率的频率成分,确保在采样时不会出现混叠现象,如下图所示:
<img src="p2.png" alt="p2" style="zoom:33%;" />
## 2. 问题2
**短时能量和短时过零率的定义,给出公式并加以说明。**
短时平均能量指在语音信号的不同时间段内,信号的能量或振幅的平均值。定义如下:
窗函数:
$$
w\left(n\right)=\left\{\begin{array}{ll}
1, & 0 \leq n \leq N-1 \\
0, & \text { 其它 }
\end{array}\right. \\
$$
短时平均能量:
$$
E_{n}=\sum_{m=-\infty}^{\infty}[x\left(m\right) w\left(n-m\right)]^{2}=\sum_{m=n-N+1}^{n}[x\left(m\right) w\left(n-m\right)]^{2}
$$
其中,$x^{2}\left( n \right) $表示语音信号在第$n$个时间段的平方振幅,$h\left(n-m\right)$表示窗函数的平方在不同时间偏移$m$下的取值。
令$h\left(n\right)=w^{2}\left(n\right)$,得到
$$
E_{n}=\sum_{m=-\infty}^{\infty} x^{2}\left(m\right) h\left(n-m\right)=x^{2}\left(n\right) \ast h\left(n\right)
$$
即$E_{n}$是语音信号在第$n$个时间段的平方振幅与窗函数平方的卷积。
其中窗函数可以有多种,常用的有:
1. 矩形窗
$$
h\left(n\right)=\left\{\begin{array}{ll}
1, & 0 \leq n \leq N-1 \\
0, & \text { 其它 }
\end{array}\right. \\
$$
2. 海明窗
$$
h\left(n\right)=\left\{\begin{array}{ll}
0.54 - 0.4\cos\left[2\pi n / \left(N - 1\right)\right], & 0 \leq n \leq N-1 \\
0, & \text { 其它 }
\end{array}\right. \\
$$
3. 汉宁窗
$$
h\left(n\right)=\left\{\begin{array}{ll}
0.5\left[1 - \cos\left(\frac{2\pi n}{N - 1}\right)\right], & 0 \leq n \leq N-1 \\
0, & \text { 其它 }
\end{array}\right. \\
$$
短时过零率指在语音信号的短时段内信号穿过水平线即振幅为0的次数。定义如下
窗函数:
$$
w\left(n\right)=\left\{\begin{array}{ll}
\frac{1}{2 N}, & 0 \leq n \leq N-1 \\
0, & \text { 其它 }
\end{array}\right. \\
$$
短时过零率:
$$
Z_{n}=\sum_{m=-\infty}^{\infty}\left|\operatorname{sgn}\left[x\left(m\right)\right]-\operatorname{sgn}\left[x\left(m-1\right)\right]\right| w\left(n-m\right) \\
\quad=\left|\operatorname{sgn}\left[x\left(n\right)\right]-\operatorname{sgn}\left[x\left(n-1\right)\right]\right| \ast w\left(n\right) \\
$$
其中$\operatorname{sgn}$是符号函数:
$$
\operatorname{sgn}\left(x\left(n\right)\right)=\left\{\begin{aligned}
1, & x\left(n\right) \geq 0 \\
-1, & x\left(n\right)<0
\end{aligned}\right.
$$
即先将信号幅度归一化为$1$(在水平线上方)和$-1$(在水平线下方),然后与窗函数进行卷积。
在噪声背景下,$\operatorname{sgn}$被修正为:
$$
\operatorname{sgn}\left(x\left(n\right)\right)=\left\{\begin{aligned}
1, & x\left(n\right) \geq \Delta \\
-1, & x\left(n\right)< -\Delta
\end{aligned}\right.
$$
以消除噪声的影响。
窗函数的作用是限制信号在时间和频率上的特性,确保在分析时局部信号的平稳性。窗函数可以防止频谱泄漏,提高分析的准确性。
## 3. 问题3
**语音信号的短时频谱的定义,如何提高短时频谱的频率分辨率?**
语音信号的短时频谱的定义:
短时频谱是指在语音信号的不同时间段内,信号的频率成分分布情况。
短时频谱可以通过对语音信号进行短时傅里叶变换计算得到,也就是将信号分割成短时段,然后对每个短时段进行傅里叶变换,得到该时段的频谱信息。
提高短时频谱的频率分辨率的方法:
增大窗函数时域窗长。
由测不准原理,窗函数时域窗长与其频域主瓣宽度的乘积不小于$\frac{1}{2}$,因此欲减小频域主瓣宽度(即频率分辨率),则需要提高窗函数时域窗长。
当然,也可以选择合适的窗函数与信号谱进行卷积,比如在同等分辨率条件下,矩形窗的窗长为海明窗窗长的$\frac{1}{2}$。
搜索资料发现,还可以使用高阶傅里叶变换方法提高短时频谱的频率分辨率,但代价是计算复杂度会增加。
## 4. 问题4
**请分析短时分析中窗函数的作用。**
- 选择分析的语音段。
- 将整个信号在短时段内截断,确保分析的语音段具有较好的时域特性。
- 时域表现为端点的截断效应,频域体现为旁瓣衰减程度。
- 时域:将信号在窗口之外置零,避免了窗口边界处的信号突变;
- 频域:窗函数的选择影响旁瓣的衰减程度,旁瓣衰减的情况决定频谱的分辨率。
- 改变窗的长度,折衷设置时间/频率分辨率。
- 窗函数的长度决定了分析的时间窗口长短。较短的窗口提供了较高的时域分辨率,但频率分辨率较低。较长的窗口则提供了较好的频率分辨率,但时域分辨率较低。因此,窗函数的长度是时域分辨率和频率分辨率之间的折衷。可以根据时域和频域的分辨率需求,选择合适的窗口长度。

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@ -0,0 +1,104 @@
<h1><center>课程作业</center></h1>
<div style="text-align: center;">
<div><span style="display: inline-block; width: 65px; text-align: center;">课程名称</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">计算机语音技术</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">作业次数</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">第3次</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">学号</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">21281280</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">姓名</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">柯劲帆</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">班级</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">物联网2101班</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">指导老师</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">朱维彬</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">修改日期</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">2023年11月1日</span></div>
</div>
---
# 1. 问题1
**请说明同态分析的基本思想,并说明同态系统三个组成系统的功能。**
同态分析的基本思想是:由于系统中的信号是非加性信号,希望能把信号变成加性信号后进行操作,再还原原本的信号。实现方法由以下步骤:
1. 通过特征变换将非线性信号转化为线性信号
2. 对线性信号进行线性滤波处理
3. 再通过逆特征变换将处理后的线性信号转换回原来的非线性信号
相应的,同态系统由以下三个部分组成:
1. **特征系统**:通过对数变换,将非线性信号转化为线性信号。
2. **线性系统**:对线性信号进行滤波处理。
3. **逆特征系统**:通过指数变换,将滤波处理后的线性信号转换回原来的非线性信号形态。
![p1](p1.png)
# 2. 问题2
**已知一语音时间序列$x\left(n\right)$,请写出其对应的复倒谱、倒谱$\hat{x}\left(n\right) $、$c\left(n\right) $的计算公式。**
复倒谱:
$$
\hat{x}\left(n \right)=\mathcal{F}^{-1}\left[\hat{X}\left(e^{j\omega}\right)\right]=\mathcal{F}^{-1}\left[\ln{X\left(e^{j\omega}\right)}\right]=\mathcal{F}^{-1}\left\{\ln{\mathcal{F\left[x\left(n\right)\right]} }\right\}
$$
倒谱:(复倒谱的实数部分)
$$
c\left(n \right)=\mathcal{F}^{-1}\left[\ln{\left|X\left(e^{j\omega}\right)\right|}\right]=\mathcal{F}^{-1}\left\{\ln{\left|\mathcal{F\left[x\left(n\right)\right]}\right|}\right\}
$$
# 3. 问题3
**请说明线性预测的基本概念,并用数学公式描述线性预测方程组的建立过程。**
线性预测指的是,根据语音样点值之间存在相关性,一个语音样点值可以用过去的若干样点值的线性组合,输入模型分析来逼近和预测。
线性预测方程为
$$
\hat{s}\left ( n \right ) = \sum_{i=1}^{P} a_i s\left ( n-i \right )
$$
其中$s\left(n\right)$是原语音信号,$\hat{s}\left ( n \right )$是预测信号,$a_i$是线性预测系数。
预测误差为
$$
e\left ( n \right ) = s\left ( n \right ) - \hat{s} \left ( n \right )
$$
均方误差准则
$$
E_n = \sum_{n}e^{2}\left ( n \right )
$$
对$a_i$求偏导,使得$E$最小
$$
\frac{\partial E}{\partial a_j} = 2\sum_{n}s\left(n\right)s\left(n-j\right)-2\sum_{i=1}^{p}a_i\sum_{n}s\left(n-i\right)s\left(n-j\right)=0
$$
构成线性方程组
$$
\sum_{n}s\left(n\right)s\left(n-j\right)=\sum_{i=1}^{p}a_i\sum_{n}s\left(n-i\right)s\left(n-j\right)
$$
$$
\sum_{i=1}^{p} a_i R_n\left( \left | j-i \right | \right) =R_n\left ( j \right ),\left ( 1\le j\le P \right )
$$
其中$R_n$是自相关函数。求解可得线性预测系数$a_1,a_2,a_3,\cdots ,a_p$。
# 4. 问题4
**已知语音信号的线性预测系数$a_i$,说明$\text{LPC}$谱的计算过程以及预测阶数$P$对$\text{LPC}$谱的影响。**
声道传递函数
$$
H\left ( z \right ) =\frac{G}{1-\sum_{i=1}^{P} a_iz^{-i}}
$$
代入$z = e^{j\omega}$,由$\text{Z}$域转换到频域,得到$\text{LPC}$谱
$$
H\left ( e^{j\omega} \right ) =\frac{G}{1-\sum_{i=1}^{P} a_ie^{-j\omega i}}
$$
预测阶数$P$对$\text{LPC}$谱的影响:
1. $P$决定了$\text{LPC}$模型的参数个数,$P$越大,能逼近信号谱的细节越多。
2. $P$越大,$\text{LPC}$谱上的共振峰个数越多,能反映出更多的共振峰信息。
3. 一般取$P$为$10\sim 14$阶,每个共振峰对应$2$阶,额外增加$2\sim 4$阶用于反映零点信息。
4. $P$的选择还要考虑分析帧长,要保证帧长大于$2$倍基音周期。

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 329 KiB

View File

@ -0,0 +1,92 @@
<h1><center>课程作业</center></h1>
<div style="text-align: center;">
<div><span style="display: inline-block; width: 65px; text-align: center;">课程名称</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">计算机语音技术</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">作业次数</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">第4次</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">学号</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">21281280</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">姓名</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">柯劲帆</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">班级</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">物联网2101班</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">课程教师</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">朱维彬</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">修改日期</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">2023年11月9日</span></div>
</div>
---
# 1. 问题1
**语音压缩的实质就是去除语音信号中冗余,请分析、归纳语音压缩编码的依据。**
- 时域冗余
- 幅度非均匀,小幅度频度高
- 相关性强
- 浊音段准周期性
- 声道变化缓慢
- 对话中的停顿
- 频域冗余
- 非均匀的功率谱密度分布
- 功率谱的结构,存在谱包络与谐波结构
- 听觉感知冗余
- 掩蔽效应。去除被掩蔽信号成分,抑制/掩盖量化噪声
- 不同频率段的感知灵敏度不同。不同频段分配不同的比特数
- 相位变化不敏感。对于相位不分/少分比特数
- 信息率冗余
- 语音包含有限个数的音素,信息率有上限,具有高压缩潜力
# 2. 问题2
**绘出$\Delta$自适应PCM系统框图说明前馈、反馈自适应的工作原理。**
![p1](p1.png)
- 前馈自适应:根据输入信号估计自适应量化台阶$\Delta\left(n\right)$。调整后的$\Delta\left(n\right)$被用来量化$x\left(n\right)$,并作为输出的一部分发送给接收端。
- 反馈自适应:根据量化器输出信号来调整自适应量化台阶$\Delta\left(n\right)$。调整后的$\Delta\left(n\right)$仅在发送端使用,不会传给接收端。接收端需要根据$c'\left(n\right)$逐步估计$\Delta\left(n\right)$,然后再对$c'\left(n\right)$进行反量化恢复。这种方案相对于前馈方案来说通道利用率更高,但是实现更复杂。
# 3. 问题3
**分析波形编码与参数编码的特点。**
- 波形编码的特点
- 逼近时域波形。语音质量好,可以很好地保留语音信号的细节
- 抗误码强,传输错误对语音质量影响较小
- 算法简单,编码复杂度低
- 压缩效率低16Kbps以下数码率语音质量下降
- 参数编码的特点
- 模型化参数。算法复杂,需要语音分析与合成
- 压缩效率高
- 语音质量较低
- 抗误码能力较差
# 4. 问题4
**根据LPC声码器原理框图分析其工作原理。**
- 假定声道模型为全极点模型用LPC分析求取这些极点作为LPC参数用来表示声道模型
- 激励源:基音周期脉冲模拟谐波结构,白噪声模拟声道的随机成分
- 编码信息:
- LPC参数表示声道模型
- 清浊及基音周期,分别用于控制表示脉冲激励的存在和脉冲激励的基音周期
- 增益,用于控制合成信号的能量

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -0,0 +1,117 @@
<h1><center>课程作业</center></h1>
<div style="text-align: center;">
<div><span style="display: inline-block; width: 65px; text-align: center;">课程名称</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">计算机语音技术</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">作业次数</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">第5次</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">学号</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">21281280</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">姓名</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">柯劲帆</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">班级</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">物联网2101班</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">课程教师</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">朱维彬</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">修改日期</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">2023年11月30日</span></div>
</div>
---
# 1. 问题1
> **HMM利用有限状态刻画语音过程请简要说明HMM三个基本参数并如何用以刻画状态序列X与语音特征序列O间的关系-计算两者间的联合概率。**
HMM$\lambda = f\left(A, B, \pi\right)$
| 参数 | 含义 |
| :---: | ------------------------------------------------------------ |
| $\pi$ | 初始状态概率。$\pi=\left[\pi_1, \pi_2, \dots ,\pi_L\right]$$\pi_j = P_r\left[x_1=S_j\right]$ |
| $A$ | 状态转移概率矩阵。$A=\left[a_{ij}\right]$$a_{ij}=P_r\left[x_t=S_j\mid X_{t-1}=S_i\right]$ |
| $B$ | 观察值概率矩阵。$B = \left[b_i\left(o_t\right)\right]$$b_i\left(o_t\right) = P_r\left[o_t\mid x_t=S_i\right]$ |
由给定的模型M计算特征序列O与状态序列X的联合概率
1. $t = 0$,根据初始状态概率向量$\pi$,选一初始状态$S_{X_0}$出现概率$\pi_{X_0}$
2. 根据B和状态$S_{X_0}$确定$o_t$的概率分布$b_{X_t}\left(o_t\right)$
3. 根据A和$S_{X_{t-1}}$确定转移到下一状态$S_{X_t}$的转移概率$a_{X_{t-1}X_{t}}$
4. $t = t + 1$,如果$t<T$跳转到2否则结束
状态序列X与语音特征序列O的联合概率为
$$
P\left(O, X\mid M\right)=\pi_{X_0}\prod_{t=1}^{T} a_{X_{t-1}X_t}b_{X_t}\left ( O_t \right )
$$
# 2. 问题2
> **基于最大似然解码的语音识别系统可用公式$\hat{W}=\underset{W}{\mathrm{argmax}}P\left (O\mid W \right )P\left ( W \right ) $加以描述,请说明公式及其中符号的意义。**
| 符号 | 意义 |
| :------------------------: | ------------------------------------------------------------ |
| $\hat{W}$ | 要估计的最优单词序列,即通过最大似然解码得到的最可能的单词序列。 |
| $O$ | 对语音信号短时分析,提取特征向量,形成观察序列。 |
| $W$ | 转写的文字序列。 |
| $P\left (O\mid W \right )$ | 在给定单词序列$W$的条件下,观测到语音信号$O$的概率。对应于声学模型,表示了语音信号与特定单词序列之间的关系。 |
| $P\left ( W \right ) $ | 单词序列$W$出现的先验概率,即在没有观测到语音信号的情况下,对单词序列的先验估计。对应于语言模型,表示了单词序列的语言结构信息。 |
整个公式$\hat{W}=\underset{W}{\mathrm{argmax}}P\left (O\mid W \right )P\left ( W \right ) $表示:在所有可能的单词序列$W$中,找到使得声学模型概率与语言模型概率之积最大化的序列,即为估计最优的单词序列。
# 3. 问题3
> **Viterbi常应用于确定HMM最优状态序列请说明最优路径搜索及对应的最优完全路径概率计算过程。**
1. **初始化**
$$
\delta_1\left(i\right)=\pi_i b_i\left(y_1\right) \left(i=1, 2, \dots, L\right)
$$
- $\delta_1(i)$:在时刻$t=1$,处于状态$i$的部分观察序列的最大概率。这是一个前向概率。
- $\pi_i$:初始时刻状态$i$的概率。
- $b_i(y_1)$:在状态$i$下生成观察符号$y_1$的概率。
2. **计算概率、最优路径**
$$
\begin{array}{c}
\delta_{n+1}\left(j\right) = \underset{i}{\mathrm{max}}\left(\delta_n\left(i\right)A_{ij}\right)\cdot b_j\left(y_{n+1}\right)\\
\varphi_{n+1}\left(j\right)= \underset{i}{\mathrm{argmax}}\left(\delta_n\left(i\right)A_{ij}\right) \left(i, j=1,2, \dots, L\right)
\end{array}
$$
- $\delta_{n+1}(j)$:在时刻$t=n+1$,处于状态$j$的部分观察序列的最大概率。
- $\underset{i}{\mathrm{max}}\left(\delta_n(i)A_{ij}\right)$:在时刻$t=n$的状态$i$到$t=n+1$的状态$j$的转移概率的最大值。
- $A_{ij}$:从状态$i$转移到状态$j$的转移概率。
- $b_j(y_{n+1})$:在状态$j$下生成观察符号$y_{n+1}$的概率。
- $\varphi_{n+1}(j)$:在时刻$t=n+1$,处于状态$j$的部分观察序列的最大概率对应的状态$t=n$的最优路径的最后一个状态。
3. **确定最优完全路径**
$$
\hat{l}_N = \underset{j}{\mathrm{argmax}}\left(\delta_N\left(j\right)\right)
$$
- $\hat{l}_N$:在时刻$t=N$,具有最大概率的状态。
4. **路径回溯**
$$
\hat{l}_n = \varphi_{n+1}\left(\hat{l}_{n+1}\right)
$$
- $\hat{l}_n$:在时刻$t=n$,具有最大概率的状态。
- $\varphi_{n+1}(\hat{l}_{n+1})$:在时刻$t=n+1$,具有最大概率的状态$t=n$的最优路径的最后一个状态。
# 4. 问题4
> **传统的语音识别系统由4个主要模块构成前端、声学模型、语言模型、解码。绘制由此构成的系统框图说明模块功能及之间的关系。**
<img src="p1.png" alt="p1" style="zoom:50%;" />
- **声学前端**:将输入的语音信号转化为可供后续处理的特征向量
- 进行预处理、分帧和特征提取
- **声学模型**:将语音信号的特征向量转化为注音符号
- 通常是基于HMM的统计模型
- **语言模型**:将注音符号转化为转写文字
- 为解码器提供根据上下文预测下一个单词/音素的能力
- **识别/解码**:搜索最大似然解
- 将声学模型和语言模型结合起来,根据这两个模型的输出进行最终的识别和解码

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
Courseware/1.绪论.pptx Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
Lab/Lab1/source/fan.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 359 KiB

BIN
Lab/Lab1/source/jiao.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 KiB

BIN
Lab/Lab1/source/jing.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 KiB

BIN
Lab/Lab1/source/ke.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

BIN
Lab/Lab1/source/p1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 MiB

BIN
Lab/Lab1/source/p2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 322 KiB

BIN
Lab/Lab1/source/pitch.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

BIN
Lab/Lab1/source/voice.wav Normal file

Binary file not shown.

BIN
Lab/Lab1/source/wo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 KiB

View File

@ -0,0 +1,145 @@
<h1><center>北京交通大学实验报告</center></h1>
<div style="text-align: center;">
<div><span style="display: inline-block; width: 65px; text-align: center;">课程名称</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">计算机语音技术</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">实验题目</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">语音工具使用</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">学号</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">21281280</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">姓名</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">柯劲帆</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">班级</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">物联网2101班</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">指导老师</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">朱维彬</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">报告日期</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">2023年10月22日</span></div>
</div>
---
## 目录
[TOC]
---
# 1. 语图1
如下图所示。上方是波形图,中间是窄带语图,下方是基频变化曲线。
![p1](p1.png)
# 2. 语图2
如下图所示。上方是波形图,中间是宽带语图和基频变化曲线,下方是标注结果。
![p2](p2.png)
# 3. 标注说明
## 3.1. “wo3”标注说明
![wo](wo.png)
`w`不构成一个单独的声母,而是与`o`结合为一个韵母。`wo3`发音过程中能量一直集中在低频成分。
## 3.2. “jiao4”标注说明
![jiao](jiao.png)
`j`是塞擦音。`jiao4`发音出现了3个阶段
1. `j`的闭塞阶段,没有高频成分,能量在低频成分;
2. `j`的擦音阶段,频率突变,高频成分增强,基频变化相对剧烈(以至于算法已经无法分析出基频);
3. `iao3`的发音阶段,能量集中在低频成分,基频变化相对平稳。
## 3.3. “ke1”标注说明
![ke](ke.png)
`k`是送气塞音。`ke1`发音也经过3个阶段
1. `k`的塞闭阶段,能量集中在低频区,没有高频成分,波形图几乎为一条直线;
2. `k`的爆发阶段,高频能量突增,能量剧烈上升,基频变化相对剧烈;
3. 送气阶段,也是`e1`的发音阶段,频谱突变,出现低频成分,之后基频逐渐减弱衰落。
## 3.4. “jing4”标注说明
![jing](jing.png)
又出现了塞擦音`j``jing4`发音也是3个阶段
1. `j`的闭塞阶段但是由于“ke1”和"jing4"两个字连读,这个阶段被跳过了;
2. `j`的擦音阶段,能量集中在高频成分,基频变化相对剧烈;
3. `ing4`的发音阶段,频谱出现低频成分,基频逐渐减弱。
## 3.5. “fan1”标注说明
![fan](fan.png)
`f`是个清擦音。`fan1`发音主要有两个阶段:
1. `f`的清擦音阶段,频谱主要集中在高频成分,基频变化剧烈;
2. `an1`的发音阶段,频谱体现为进入较平稳的低频区,基频平稳。
# 4. 基频分析
![pitch](pitch.png)
该图上的数字表示基频在该点的置信度。将散点连起来既是基频曲线。没有选中的点是基频计算算法计算出置信度较小的基频点,可以人工挑选以修改基频曲线。
通过Praat自动计算的基频曲线基频分析如下
1. `wo3`的基频总体下降表现第3声的音调总体降低的趋势。
2. 在`wo3``jiao4`之间出现了高频噪声。
3. `jiao4`也是基频总体下降表现第4声的音调总体降低的趋势。
4. `ke1`基频首先由高至低,这是因为塞音`k`存在一个爆发阶段产生大量的高频成分然后基频平稳因为第1声的发音音调是平稳的。
5. `jing4``jiao4`相似也是基频总体下降表现第4声的音调总体降低的趋势。
6. `fan1``ke1`的基频相似都是由于声母存在擦音阶段或爆破阶段导致一开始基频较高然后发音声调为第1声导致后来基频趋于平稳。
总体来说基频在100hz到200Hz之间。估计最高基频为210Hz最低基频在100Hz平均为170Hz。
使用Praat导出基频的最高、最低、平均值如下
最高基频:
<img src="max pitch.png" alt="max pitch" style="zoom:50%;" />
最低基频:
<img src="min pitch.png" alt="min pitch" style="zoom:50%;" />
平均基频:
<img src="mean pitch.png" alt="mean pitch" style="zoom:50%;" />
除了最高基频Praat预测有误差之外Praat预测的最低基频和平均基频都与我的估计差别不大。

Binary file not shown.

Binary file not shown.

BIN
Lab/Lab2/code/tang1.wav Normal file

Binary file not shown.

623
Lab/Lab2/code/test.ipynb Normal file

File diff suppressed because one or more lines are too long

179
Lab/Lab2/code/test.py Normal file
View File

@ -0,0 +1,179 @@
from typing import Optional
import scipy.io.wavfile as wav
import numpy as np
import matplotlib.pyplot as plt
import ipdb
def hamming(frame_length: int) -> np.ndarray:
# frame_length - 窗长
n = np.arange(frame_length)
h = 0.54 - 0.4 * np.cos(2 * np.pi * n / (frame_length - 1))
return h
def delta_sgn(x: np.ndarray) -> np.ndarray:
# x - 语音信号
sound = x
threshold = np.max(np.abs(sound)) / 20
negative_sound = sound + threshold
negative_sound -= np.abs(negative_sound)
positive_sound = sound - threshold
positive_sound += np.abs(positive_sound)
sound = negative_sound + positive_sound
return np.sign(sound)
def ampf(
x: np.ndarray, FrameLen: Optional[int] = 128, inc: Optional[int] = 90
) -> np.ndarray:
# x - 语音时域信号
# FrameLen - 每一帧的长度
# inc - 步长
frames = []
for i in range(0, len(x) - FrameLen, inc):
frame = x[i : i + FrameLen]
frames.append(frame)
frames = np.array(frames)
h = hamming(frame_length=FrameLen) # 海明窗
amp = np.dot(frames**2, h.T**2).T / FrameLen
return amp
def zcrf(
x: np.ndarray, FrameLen: Optional[int] = 128, inc: Optional[int] = 90
) -> np.ndarray:
# x - 语音时域信号
# FrameLen - 每一帧的长度
# inc - 步长
sound = x
sgn_sound = np.sign(sound)
dif_sound = np.abs(sgn_sound[1:] - sgn_sound[:-1])
h = np.ones((FrameLen,)) / (2 * FrameLen)
frames = []
for i in range(0, len(dif_sound) - FrameLen, inc):
frame = dif_sound[i : i + FrameLen]
frames.append(frame)
frames = np.array(frames)
zcr = np.dot(frames, h.T).T
return zcr
def zcrf_delta(
x: np.ndarray, FrameLen: Optional[int] = 128, inc: Optional[int] = 90
) -> np.ndarray:
# x - 语音时域信号
# FrameLen - 每一帧的长度
# inc - 步长
sound = x
sgn_sound = delta_sgn(sound)
dif_sound = np.abs(sgn_sound[1:] - sgn_sound[:-1])
h = np.ones((FrameLen,)) / (2 * FrameLen)
frames = []
for i in range(0, len(dif_sound) - FrameLen, inc):
frame = dif_sound[i : i + FrameLen]
frames.append(frame)
frames = np.array(frames)
zcr = np.dot(frames, h.T).T
return zcr
def analyze_sound(
filename: str, FrameLen: Optional[int] = 128, inc: Optional[int] = 90
) -> None:
sr, sound_array = wav.read(filename)
sound_array = sound_array.T[0, :] if sound_array.ndim != 1 else sound_array
sound_array = sound_array / np.max(np.abs(sound_array)) # 归一化
amp = ampf(sound_array, FrameLen, inc)
zcr = zcrf_delta(sound_array, FrameLen, inc)
rescale_rate = len(sound_array) / amp.shape[0]
frameTime = np.arange(len(amp)) * rescale_rate
# 边界检测
x1 = []
x2 = []
x3 = []
amp2 = np.min(amp) + (np.max(amp) - np.min(amp)) / 20
zcr2 = np.min(zcr) + (np.max(zcr) - np.min(zcr)) / 18
threshold_len = 6
state = 1
for i in range(threshold_len, len(amp) - threshold_len):
if state == 1:
if np.all(zcr[i : i + threshold_len] > zcr2):
x1.append(i * rescale_rate)
state = 2
elif state == 2:
if np.all(amp[i : i + threshold_len] > amp2):
x3.append(i * rescale_rate)
state = 3
if (
state != 1
and np.all(amp[i : i + threshold_len] < amp2)
and np.all(zcr[i : i + threshold_len] < zcr2)
):
x2.append(i * rescale_rate)
state = 1
# 绘制语音波形、短时能量、短时过零率
plt.figure(figsize=(12, 8))
# 语音波形
plt.subplot(3, 1, 1)
plt.plot(sound_array)
plt.title("Waveform")
for boundary in x1:
plt.axvline(x=boundary, color="r", linestyle="--", linewidth=0.5)
for boundary in x2:
plt.axvline(x=boundary, color="b", linestyle="--", linewidth=0.5)
for boundary in x3:
plt.axvline(x=boundary, color="g", linestyle="--", linewidth=0.5)
# 短时能量
plt.subplot(3, 1, 2)
plt.plot(frameTime, amp, label="Energy")
plt.axhline(y=amp2, color="r", linestyle="--", label="Energy Threshold")
plt.legend()
plt.title("Short-time Energy")
for boundary in x1:
plt.axvline(x=boundary, color="r", linestyle="--", linewidth=0.5)
for boundary in x2:
plt.axvline(x=boundary, color="b", linestyle="--", linewidth=0.5)
for boundary in x3:
plt.axvline(x=boundary, color="g", linestyle="--", linewidth=0.5)
# 短时过零率
plt.subplot(3, 1, 3)
plt.plot(frameTime, zcr, label="Zero Crossing Rate")
plt.axhline(y=zcr2, color="r", linestyle="--", label="ZCR Threshold")
plt.legend()
plt.title("Short-time Zero Crossing Rate")
# 显示语音端点和清/浊音边界
for boundary in x1:
plt.axvline(x=boundary, color="r", linestyle="--", linewidth=0.5)
for boundary in x2:
plt.axvline(x=boundary, color="b", linestyle="--", linewidth=0.5)
for boundary in x3:
plt.axvline(x=boundary, color="g", linestyle="--", linewidth=0.5)
plt.tight_layout()
plt.show()
if __name__ == "__main__":
analyze_sound("tang1.wav", FrameLen=128, inc=90)

BIN
Lab/Lab2/code/voice.wav Normal file

Binary file not shown.

Binary file not shown.

BIN
Lab/Lab2/source/compare.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

View File

@ -0,0 +1,360 @@
<h1><center>实验报告</center></h1>
<div style="text-align: center;">
<div><span style="display: inline-block; width: 65px; text-align: center;">课程名称</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">计算机语音技术</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">实验题目</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">短时分析应用</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">学号</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">21281280</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">姓名</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">柯劲帆</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">班级</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">物联网2101班</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">指导老师</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">朱维彬</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">报告日期</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">2023年10月29日</span></div>
</div>
---
# 目录
[TOC]
---
# 1. 短时能量和短时过零率函数
**添加短时时域参数函数:**
- **短时能量**
- **短时过零率**
## 1.1. 计算短时能量
短时能量指在语音信号的不同时间段内,信号的能量或振幅的平均值。
短时能量的计算公式如下:
$$
E_{n}=\sum_{m=-\infty}^{\infty}[x\left(m\right) h\left(n-m\right)]^{2}=\sum_{m=n-N+1}^{n}[x\left(m\right) h\left(n-m\right)]^{2}
$$
其中$h\left(n\right)$为窗函数,这里选择为海明窗:
$$
h\left(n\right)=\left\{\begin{array}{ll}
0.54 - 0.4\cos\left[2\pi n / \left(N - 1\right)\right], & 0 \leq n \leq N-1 \\
0, & \text { others }
\end{array}\right. \\
$$
因此使用Python定义计算海明窗的函数如下。numpy库也有内置的海明窗函数这里手动实现和numpy的接口一致
```python
def hamming(frame_length:int) -> np.ndarray:
# frame_length - 窗长
n = np.arange(frame_length)
h = 0.54 - 0.4 * np.cos(2 * np.pi * n / (frame_length - 1))
return h
```
**计算短时能量的算法:将每一帧的语音信号提取出来,乘上窗长并平方,然后求和取平均,即可得出该帧的短时能量。将窗口移动步长个单位,重复前面的流程,直至分析完整段语音。**
使用Python实现如下。
```python
def ampf(x: np.ndarray, FrameLen: Optional[int] = 128, inc: Optional[int] = 90) -> np.ndarray:
# x - 语音时域信号
# FrameLen - 每一帧的长度
# inc - 步长
frames = []
for i in range(0, len(x) - FrameLen, inc):
frame = x[i : i + FrameLen]
frames.append(frame)
frames = np.array(frames)
h = hamming(frame_length=FrameLen)[::-1] / FrameLen
amp = np.dot(frames ** 2, h.T ** 2).T
return amp
```
画出`tang1`的短时能量曲线如下:
![picture-amp](picture-amp.png)
短时能量体现了该帧的振幅,可以表征韵母的发声和结束。
## 1.2. 计算短时过零率
短时过零率指在语音信号的短时段内信号穿过水平线即振幅为0的次数。定义如下
窗函数:
$$
w\left(n\right)=\left\{\begin{array}{ll}
\frac{1}{2 N}, & 0 \leq n \leq N-1 \\
0, & \text { others }
\end{array}\right. \\
$$
短时过零率:
$$
Z_{n}=\sum_{m=-\infty}^{\infty}\left|\operatorname{sgn}\left[x\left(m\right)\right]-\operatorname{sgn}\left[x\left(m-1\right)\right]\right| w\left(n-m\right)
$$
其中$\operatorname{sgn}$是符号函数:
$$
\operatorname{sgn}\left(x\left(n\right)\right)=\left\{\begin{array}{ll}
1, & x\left(n\right) \geq 0 \\
-1, & x\left(n\right)<0
\end{array}\right.
$$
**计算短时过零率的算法:先从语音信号中计算出过零序列(经过$\operatorname{sgn}$转化后,后一信号减前一信号)。然后将每一帧的语音信号对应的过零序列提取出来,求和并除以帧长,即为该帧的过零率。将窗口移动步长个单位,重复前面的流程,直至分析完整段语音。**
使用Python实现如下
```python
def zcrf(x: np.ndarray, FrameLen: Optional[int] = 128, inc: Optional[int] = 90) -> np.ndarray:
# x - 语音时域信号
# FrameLen - 每一帧的长度
# inc - 步长
sound = x
sgn_sound = np.sign(sound)
dif_sound = np.abs(sgn_sound[1:] - sgn_sound[:-1])
frames = []
for i in range(0, len(dif_sound) - FrameLen, inc):
frame = dif_sound[i : i + FrameLen]
frames.append(frame)
frames = np.array(frames)
h = np.ones((FrameLen,)) / (2 * FrameLen)
zcr = np.dot(frames, h.T).T
return zcr
```
画出`tang1`的短时过零率曲线如下:
![picture-zcrf](picture-zcrf.png)
短时过零率体现了该帧的高频声音,可以表征声母的发声。
# 2. 边界检测
**添加边界检测器,基于短时能量、短时过零率,实现边界检测功能,包括**
- **语音端点检测——起始边界x1、终止边界x2**
- **清/浊边界检测x3**
我将每个发音分为3个阶段
1. 未发声阶段:此时短时能量和短时过零率都很低
2. 声母阶段:此时声母的塞音、擦音和塞擦音等会产生大量的高频声波,过零率较大;但是此时韵母还没发出,短时能量较低。这一阶段的开始为`x1`
3. 韵母阶段此时韵母发出频率趋于平稳和下降因此此时过零率下降但短时能量激增并逐渐减少直至发声完毕回到1阶段。这一阶段的开始为`x3`,结束为`x2`
**一开始将阶段初始化为`1`未发声阶段;接着当过零率高于阈值时,进入`2`声母阶段,添加`x1`;接着当短时能量高于阈值时,进入`3`声母阶段,添加`x3`;在进入`2``3`阶段后,当短时能量和短时过零率同时低于阈值时,重置为`1`未发声阶段,添加`x2`。**
**另外还设置了一个阈值宽度,当语音信号在大于阈值宽度的信号段满足条件才算通过。**
使用Python实现如下
```python
sr, sound_array = wav.read(filename)
sound_array = sound_array.T[0, :] if sound_array.ndim != 1 else sound_array # 双通道改单通道
sound_array = sound_array / np.max(np.abs(sound_array)) # 归一化
amp = ampf(sound_array, FrameLen, inc)
zcr = zcrf_delta(sound_array, FrameLen, inc)
rescale_rate = len(sound_array) / amp.shape[0]
frameTime = np.arange(len(amp)) * rescale_rate
# 将曲线图拉伸至和语音信号图一样长,方便分析
x1 = []
x2 = []
x3 = []
amp2 = np.min(amp) + (np.max(amp) - np.min(amp)) * 0.05
zcr2 = np.min(zcr) + (np.max(zcr) - np.min(zcr)) * 0.04
threshold_len = 6
state = 1
for i in range(threshold_len, len(amp) - threshold_len):
if state == 1:
if np.all(zcr[i : i + threshold_len] > zcr2):
x1.append(i * rescale_rate)
state = 2
elif state == 2:
if np.all(amp[i : i + threshold_len] > amp2):
x3.append(i * rescale_rate)
state = 3
if state != 1 and np.all(amp[i : i + threshold_len] < amp2) and np.all(zcr[i : i + threshold_len] < zcr2):
x2.append(i * rescale_rate)
state = 1
```
阈值参数的选取在下一节中分析。
# 3. 绘制图像与分析
**绘制语音边界检测图,包括**
- **语音波形、短时能量、短时过零率**
- **自动检测结果:音段起始/终止边界、清音/浊音边界**
使用Python实现如下
```python
# 绘制语音波形、短时能量、短时过零率
plt.figure(figsize=(12, 8))
# 语音波形
plt.subplot(3, 1, 1)
plt.plot(sound_array)
plt.title("Waveform")
for boundary in x1:
plt.axvline(x=boundary, color="r", linestyle="--", linewidth=0.8)
for boundary in x2:
plt.axvline(x=boundary, color="b", linestyle="--", linewidth=0.8)
for boundary in x3:
plt.axvline(x=boundary, color="g", linestyle="--", linewidth=0.8)
# 短时能量
plt.subplot(3, 1, 2)
plt.plot(frameTime, amp, label="Energy")
plt.axhline(y=amp2, color="r", linestyle="--", label="Energy Threshold", linewidth=0.8)
plt.legend()
plt.title("Short-time Energy")
for boundary in x1:
plt.axvline(x=boundary, color="r", linestyle="--", linewidth=0.8)
for boundary in x2:
plt.axvline(x=boundary, color="b", linestyle="--", linewidth=0.8)
for boundary in x3:
plt.axvline(x=boundary, color="g", linestyle="--", linewidth=0.8)
# 短时过零率
plt.subplot(3, 1, 3)
plt.plot(frameTime, zcr, label="Zero Crossing Rate")
plt.axhline(y=zcr2, color="r", linestyle="--", label="ZCR Threshold", linewidth=0.8)
plt.legend()
plt.title("Short-time Zero Crossing Rate")
for boundary in x1:
plt.axvline(x=boundary, color="r", linestyle="--", linewidth=0.8)
for boundary in x2:
plt.axvline(x=boundary, color="b", linestyle="--", linewidth=0.8)
for boundary in x3:
plt.axvline(x=boundary, color="g", linestyle="--", linewidth=0.8)
plt.tight_layout()
plt.show()
```
`x1`语音开始边界标记为红色,`x2`语音结束边界标记为蓝色,将`x3`声韵母边界标记为绿色。
画出`tang1`的语音边界检测图如下:
![picture-tang1](picture-tang1.png)
一共有3个参数阈值宽度、短时能量阈值、短时过零率阈值
观察短时能量曲线和短时过零率曲线可见,声母开始时,短时能量曲线有一个小峰值,而短时过零率曲线出现大峰值,因此短时能量阈值必须高于该小峰值,才不会将声母开始判定为韵母开始。
韵母开始时,短时能量曲线出现大峰值,因此短时能量阈值应在大峰值和小峰值之间,且尽可能偏小,才能准确预测声韵母边界,经过多次实验,将短时能量阈值定为最大值的$5\%$;而短时过零率曲线回落,并在低值维持一段时间。因此短时过零率阈值要小于这个低值,经过多次实验,将短时过零率阈值定为最大值的$4\%$。
经过多次实验,将阈值宽度定为$6$帧。
从检测结果来看,上述参数的选择能够较为准确地区分三个边界。但是声韵母边界有少许滞后于真实边界。
# 4. 自录制语音检测、分析与算法优化
自录制一段语音:“计算机语音技术”,检测与绘图如下:
![self_record](self_record.png)
很显然,噪声较大,严重干扰了分析。
分析可知,噪声主要影响的是短时过零率。因此,我对短时过零率算法进行了优化,采用了噪声背景下的修正$\operatorname{sgn}$函数:
$$
\operatorname{sgn}\left(x\left(n\right)\right)=\left\{\begin{array}{ll}
1, & x\left(n\right) \geq \Delta \\
-1, & x\left(n\right)< -\Delta\\
0, & \text{others}
\end{array}\right.
$$
在具体的实现中,我使用矩阵运算语音信号,逐采样点判断$x\left(n\right)$和$\pm \Delta$的大小既不经济,也不优雅。因此,我首先将$x\left(n\right)$进行了变换,即将修正$\operatorname{sgn}$函数改写为:
$$
\operatorname{sgn}\left(x\left(n\right)\right)=\left\{\begin{array}{ll}
1, & x\left(n\right) \geq 0 \wedge x\left(n\right) - \Delta \geq 0\\
-1, & x\left(n\right) < 0 \wedge x\left(n\right) + \Delta< 0\\
0, & \text{others}
\end{array}\right.
$$
相当于正负值信号都向横坐标轴缩减了$\Delta$,再进行普通的$\operatorname{sgn}$操作。
所以,首先将语音信号减去阈值$\Delta$,去掉负值信号,得到正值信号;将语音信号加上阈值$\Delta$,去掉正值信号,得到负值信号。再将两者相加合并,得到处理后的语音信号。最后,进行普通的普通的$\operatorname{sgn}$函数操作。
Python实现如下
```python
def delta_sgn(x: np.ndarray) -> np.ndarray:
# x - 语音信号
sound = x
threshold = np.max(np.abs(sound)) * 0.05
negative_sound = sound + threshold
negative_sound -= np.abs(negative_sound)
positive_sound = sound - threshold
positive_sound += np.abs(positive_sound)
sound = (negative_sound + positive_sound) / 2
sound = np.sign(sound)
return sound
```
画出向横坐标轴缩减了$\Delta$的语音信号(下)与原语音信号(上)的对比图:
![compare](compare.png)
很明显,噪声几乎被消除了。
接下来使用上面定义的`delta_sign()`函数,重复之前的计算进行分析和画图:
![self_record_optimed](self_record_optimed.png)
可以看到,算法能够在噪声下辨认出`ji4``suan4``ji1``ji4``shu4`这4个发音的发声起止边界和声韵母边界但是`yu3``yin1`两个发音没有声母,在仅用短时能量和短时过零率两个指标的条件下无法正常检测出边界。另外由于算法抑制了一部分噪声,韵母发音的最后一小部分被消除了,因此检测到的发音结束边界较正确的结束边界有所提前。
将改进后的算法应用到`tang1`的音频中,检测结果如下:
![tang1_optimed](tang1_optimed.png)
发现其声母`t`阻塞阶段的高频声音被抑制了,但由于音量较大,没有被作为噪声消除,依然能被正常识别。但是发音末尾的少部分被当作噪声消除了,导致发音结束边界较正确的结束边界有所提前。检测结果总体上正确。

Binary file not shown.

BIN
Lab/Lab3/code/tang1.wav Normal file

Binary file not shown.

137
Lab/Lab3/code/test.ipynb Normal file

File diff suppressed because one or more lines are too long

55
Lab/Lab3/code/test.py Normal file
View File

@ -0,0 +1,55 @@
import scipy.io.wavfile as wav
from scipy import signal
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import ipdb;
# 读取音频文件
filename = "./tang1.wav"
sample_rate, sound_array = wav.read(filename)
sound_array = sound_array.T[0, :] if sound_array.ndim != 1 else sound_array
sound_array = sound_array / np.max(np.abs(sound_array)) # 归一化
frame_length = int(sample_rate * 0.01)
num_frames = len(sound_array) // frame_length
autocorrelation = np.zeros((num_frames, frame_length))
autocorrelation_of_candidates = np.zeros((num_frames, frame_length))
min_peak_threshold = min(sample_rate // 400, frame_length)
max_peak_threshold = min(sample_rate // 80, frame_length)
for n in range(num_frames):
frame = sound_array[n * frame_length: (n + 1) * frame_length]
autocorrelation[n, :] = signal.correlate(frame, frame, mode='full')[frame_length - 1:]
# 基频阈值为80-400Hz则基音周期即延迟t最小为sample_rate/400最大为sample_rate/80
# 本应该使用峰值的延迟作为基音周期的候选值,但是发现峰值(局部最大值)并不好判断,同时一帧内的点数不多,因此将阈值内的所有点都作为候选点
# 那么将不在阈值内的自相关系数置为一个非常小的数,从而不让算法选择不在阈值内的基音周期
autocorrelation_of_candidates[n, :] = np.pad(
autocorrelation[n, min_peak_threshold : max_peak_threshold],
(min_peak_threshold, max(frame_length - max_peak_threshold, 0)),
mode='constant',
constant_values=-30.0,
)
dist = -autocorrelation
cost = np.zeros((num_frames, frame_length))
path = np.zeros((num_frames, frame_length))
for n in range(num_frames - 1):
for j in range(min_peak_threshold, max_peak_threshold):
# f0 = sample_rate / candidate
cost[n + 1, j] = dist[n + 1, j] + np.min(
cost[n, :] + np.abs(sample_rate / np.arange(frame_length) - sample_rate / j)
)
path[n + 1, j] = np.argmin(
cost[n, :] + np.abs(sample_rate / np.arange(frame_length) - sample_rate / j)
)
l_hat = np.zeros(num_frames, dtype=np.int32)
l_hat[num_frames - 1] = np.argmin(cost[num_frames - 1, :])
for n in range(num_frames - 2, -1, -1):
l_hat[n] = path[n + 1, l_hat[n + 1]]
f0 = sample_rate / l_hat

BIN
Lab/Lab3/code/voice.wav Normal file

Binary file not shown.

View File

@ -0,0 +1,88 @@
clc;clear all;close all;
warning('off')
%[x,fs,nbits]=wavread('tang1.wav'); %
global fs;
[x,fs] = audioread('tang1.wav'); %
%info = audioinfo('tang1.wav');
%nbits = info.BitsPerSample;
x = x / max(abs(x)); %[-1,1]
%
kl = round(1 / 500 * fs); %500Hz
kr = round(1 / 80 * fs); %80Hz
N = 3 * kr; %
inc = round(fs / 100); %10ms
%
subplot(3, 1, 1);
plot(x);
axis([1 length(x) -1 1]) %xy
xlabel('');
ylabel('Speech');
legend('FrameLen = 552');
%
subplot(3, 1, 2);
A = enframe(x, N, inc);
R = zeros(size(A));
l = zeros(1, size(R, 1));
f = zeros(1, size(R, 1));
Can = zeros(1, 10);
acCan = zeros(1, 10);
CostF = zeros(1, 10);
it = 0;
for n = 1:size(R, 1)
R(n, :) = autocorr(A(n, :), N - 1);
Can_ = Can;
CostF_ = CostF;
[acCan,Can] = findpeaks(R(n, kl:kr), 'MinPeakHeight', R(n, 1) * 0.25, 'MinPeakProminence', 0.9);
Can = Can + kl - 1;
sz = size(Can, 2);
if sz ~= 0
it = it + 1;
if it == 1
CostF = dist(acCan);
else
CostF = zeros(1, sz);
Path = zeros(1, sz);
CostT = diff(Can, Can_);
for j = 1:sz
[CostF(j), Path(j)] = min(CostF_ + CostT(j, :));
CostF = CostF + dist(acCan);
end
end
[~, l(n)] = min(CostF);
ff = f0(Can);
plot(n, ff, '.');
hold on;
f(n) = ff(l(n));
else
it = 0;
end
end
subplot(3, 1, 3);
f = medfilt1(f, 5);
stem(f, 'MarkerSize',3);
xlabel('(n)');
ylabel('(Hz)');
function f = f0(Can)
global fs;
f = fs ./ Can;
end
function dis = dist(ac)
dis = -log(ac);
end
function df = diff(Can1, Can2)
n = size(Can1, 2);
m = size(Can2, 2);
df = zeros(n, m);
for i = 1:n
for j = 1:m
df(i, j) = abs(f0(Can1(i)) - f0(Can2(j)));
end
end
end

Binary file not shown.

Binary file not shown.

BIN
Lab/Lab3/source/p1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

BIN
Lab/Lab3/source/p2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

BIN
Lab/Lab3/source/p3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
Lab/Lab3/source/p4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

BIN
Lab/Lab3/source/p5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

BIN
Lab/Lab3/source/p6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

BIN
Lab/Lab3/source/p7.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

View File

@ -0,0 +1,352 @@
<h1><center>实验报告</center></h1>
<div style="text-align: center;">
<div><span style="display: inline-block; width: 65px; text-align: center;">课程名称</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">计算机语音技术</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">实验题目</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">实验3</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">学号</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">21281280</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">姓名</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">柯劲帆</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">班级</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">物联网2101班</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">指导老师</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">朱维彬</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">报告日期</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">2023年10月13日</span></div>
</div>
---
[TOC]
---
# 1. 自相关系数计算和基音周期候选点集
- **基于自相关系数AC的局部极大值构成基音周期候选点集通过预设基频范围进行初步筛选**
## 1.1. 计算自相关系数
自相关算法能够体现语音信号的时域重复性。
基于某帧语音信号的自相关函数的在某一时延$t$(单位为采样点)的大小,可以推断该帧语音信号的基音周期是$t$的可能性:$t$点自相关系数越大,基音周期是$t$的概率越大;反之亦然。
因此,可以通过计算语音信号中各帧的自相关函数来推断各帧的基音周期。
自相关函数的计算方法:
$$
\hat{R}_n\left(k\right) = \sum_{m=0}^{N-1}x\left(n+m\right)x\left(n+m+k\right)
$$
其中$\hat{R}_n\left(k\right)$是第$k$帧的自相关函数,其中$0\le k < N$$N$为语音的分帧数
代码实现如下:
```python
# 读取音频文件
filename = "./tang1.wav"
sample_rate, sound_array = scipy.io.wavfile.read(filename)
sound_array = sound_array.T[0, :] if sound_array.ndim != 1 else sound_array
sound_array = sound_array / np.max(np.abs(sound_array)) # 归一化
frame_length = int(sample_rate * 0.01)
num_frames = len(sound_array) // frame_length
autocorrelation = np.zeros((num_frames, frame_length)) # 每帧的自相关函数
autocorrelation_of_candidates = np.zeros((num_frames, frame_length))
for n in range(num_frames):
frame = sound_array[n * frame_length: (n + 1) * frame_length] # 提取单帧
autocorrelation[n, :] = scipy.signal.correlate(frame, frame, mode='full')[frame_length - 1:]
```
代码的逻辑为:
1. 读取音频文件并进行预处理;
2. 设置帧长和分帧数,以及初始化自相关函数的记录变量;
3. 逐帧进行自相关计算。
## 1.2. 选取基音周期候选点集
得到自相关函数`autocorrelation`后,下一步就是取各帧的局部极大值构成基音周期候选点集。
但是在实际实验中局部极大值的提取较为困难要么就是自相关函数波动严重局部极大值非常多要么就是自相关函数单调变化或平滑变化局部极大值几乎不存在。由于后续的DP算法需要每一帧都有基音周期候选点集所以局部极大值数量少会严重影响算法的稳定性。
因此,考虑到一帧的长度不大,我**将符合人声基频范围的所有基音周期都视为候选点**。那么基音周期候选点的范围限制为:
$$
\frac{1}{min\times \frac{1}{sample \space rate}} \le 80\rm{Hz}\Longrightarrow min \ge \frac{sample \space rate}{80\rm{Hz}}
$$
同理,
$$
\frac{1}{max\times \frac{1}{sample \space rate}} \ge 400\rm{Hz}\Longrightarrow max \le \frac{sample \space rate}{400\rm{Hz}}
$$
综上,基音周期候选点的范围为
$$
\left [\frac{sample \space rate}{400\rm{Hz}}, \frac{sample \space rate}{80\rm{Hz}}\right ]
$$
为了让DP算法只选择在阈值内的基音周期即不选择阈值外的基音周期所以将阈值外的自相关系数置为一个较小的数从而让DP算法选择阈值外基音周期的代价变高。
代码实现如下:
```python
min_peak_threshold = min(sample_rate // 400, frame_length)
max_peak_threshold = min(sample_rate // 80, frame_length)
autocorrelation_of_candidates = autocorrelation
autocorrelation_of_candidates[:, 0:min_peak_threshold] = np.min(autocorrelation) - 5.
autocorrelation_of_candidates[:, max_peak_threshold:] = np.min(autocorrelation) - 5.
```
最终,`tang1.wav`的自相关函数计算结果如下所示,
<img src="p1.png" alt="p1" style="zoom:67%;" />
$x$轴为帧,$y$轴为候选点,$z$轴为自相关函数。不在人声基频范围内的点的自相关系数被置为一个很低的数值(蓝色区域所示)。
# 2. 动态规划DP算法
- **采用动态规划DP算法通过代价函数`CostFunction() `涉及目标、转移代价,利用帧同步搜索代价最小路径,检测出基频**
DP算法的步骤如下
1. **初始化第$1$帧的代价函数**
$$
CostF\left(1, i\right) = Dist\left(ac\left({Can}_{1}\left(i\right)\right)\right) \left ( i=1, 2, \dots, L \right )
$$
其中:
- $CostF\left(n, i\right)$表示第$n$帧的第$i$个基音周期候选点的累计代价;
- $Dist\left(\right)$为距离测度;
- $ac\left(\right)$表示自相关函数序列;
- ${Can}_{n}\left(i\right)$表示第$n$帧的第$i$个基音周期候选点;
- $L$表示候选点数。
我将$Dist\left(\right)$的计算设置为
$$
Dist\left(ac\left({Can}_{1}\left(i\right)\right)\right) = - \operatorname{normalize} \left(ac\left({Can}_{1}\left(i\right)\right)\right) \times 2 \times10^6
$$
其中$\operatorname{normalize} \left(\right)$表示归一化,这是为了控制不同的音频计算得到的不同自相关系数在固定的范围。
代码实现如下:
```python
costF = np.zeros((num_frames, frame_length))
path = np.zeros((num_frames, frame_length))
dist = -(autocorrelation_of_candidates - np.min(autocorrelation_of_candidates)) / (np.max(autocorrelation_of_candidates) - np.min(autocorrelation_of_candidates)) * 2e3
costF[0, :] = dist[0, :]
```
代码声明了代价记录变量和转移路径记录变量,然后初始化第$1$帧的代价函数。
2. **计算每帧的代价函数**
$$
\begin{array}{c}
CostF\left(n+1, j\right) = \underset{i}{\operatorname{min}}\left(CostF\left(n, i\right)+CostT\left(n, i, j\right)\right)+CostG\left(n+1, j\right)\\
Path = \underset{i}{\operatorname{argmin}}\left(CostF\left(n, i\right)+CostT\left(n, i, j\right)\right)\left ( i,j=1, 2, \dots, L \right )
\end{array}
$$
其中:
- $CostT\left(n, i, j\right)$表示转移代价:第$n$帧中第$i$个候选点的基频与第$n+1$帧中第$j$个候选点的基频之差,并进行归一化;
- $CostG\left(n+1, j\right)$为目标代价:等于$Dist\left(ac\left({Can}_{n+1}\left(j\right)\right)\right)$的值。
这一步是要计算得到的结果满足两个评价指标的权衡:
- 自相关系数尽可能大,即基音周期正确概率尽可能大;
- 基频的变化尽可能小,即基音周期的变化尽可能小。
这两个指标都进行了归一化操作,即都在$\left[0, 1\right]$之间,只需要调整它们的权重即可人工控制权衡的决策。这里,我将自相关系数相关的权重设置为$2 \times10^6$,基音周期变化的权重设置为$1$。
代码实现如下:
```python
for n in range(num_frames - 1):
for j in range(min_peak_threshold, max_peak_threshold):
costT = np.abs(sample_rate / np.arange(frame_length)[1:] - sample_rate / j)
costT = (costT - np.min(costT)) / (np.max(costT) - np.min(costT))
costG = dist[n + 1, j]
costF[n + 1, j] = costG + np.min(costF[n, 1:] + costT)
path[n + 1, j] = np.argmin(costF[n, 1:] + costT) + 1
```
代码循环计算了每帧的代价函数。
3. **确定最优路径**
$$
\hat{l}_N=\underset{j}{\operatorname{argmin}}\left(CostF\left(N, j\right)\right)
$$
即找到累计代价最小的结束帧作为预测结果的结束帧。
代码实现如下:
```python
l_hat = np.zeros(num_frames, dtype=np.int32)
l_hat[num_frames - 1] = np.argmin(costF[num_frames - 1, 1:]) + 1
```
4. **路径回溯**
$$
\begin{array}{c}
\hat{l}_n=Path\left(n+1, \hat{l}_{n+1}\right)\\
F0\left [ n \right ] = F0\left ( {Can}_{n}\left(\hat{l}_n\right) \right )
\end{array}
$$
即从后一帧往前推出使得代价最小的前一帧(代价最小的转移路径已经在`Path`变量中记录),然后计算出该帧的基频。
由候选点计算基频的公式为:
$$
F0\left ({Can}_{n}\left(i\right) \right ) = \frac{sample \space rate}{{Can}_{n}\left(i\right)}
$$
代码实现如下:
```python
for n in range(num_frames - 2, -1, -1):
l_hat[n] = path[n + 1, l_hat[n + 1]]
f0 = sample_rate / l_hat
```
运行以上代码,并画出语音信号及基频变化如下:
![p2](p2.png)
使用Praat进行基频检测预测基频如下
![p3](p3.png)
预测结果相近,表明算法基本正确。
# 3. 绘制图像与分析
- **自行录制一段连续语音发音,对检测结果进行分析,针对观察到的问题优化算法**
使用实验1中录制的“wo3 jiao4 ke1 jing4 fan1”作为测试音频进行基频分析。
自相关函数如下:
<img src="p4.png" alt="p4" style="zoom:67%;" />
发现自相关函数比单音节的`tang1`的更加复杂,但是可以明显发现五个发音之间由四个空隙隔开——在这四个空隙中,自相关函数值相对较低,印证了发音间隙基频变化大的事实。
使用过DP算法计算后画出基频曲线
![p5](p5.png)
虽然可以大致判断基频变化情况但是不明显。分析发现基频变化太大了因此需要调整转移代价的权重增加基频转移对DP算法决策的惩罚。
因此,将
```python
dist = -autocorrelation_of_candidates * 2e3
```
修改为
```python
dist = -autocorrelation_of_candidates * 1e1
```
其余不变。再次运行并绘图:
![p6](p6.png)
此时,基频更加集中,变化趋势较为明显。
与Praat的检测结果进行比对
![p7](p7.png)
有较高的相似性,说明算法有效。
对于算法的优化调整,我只想到调整目标代价和转移代价两者之间的平衡权重进行优化。这个优化方法只能靠手动且经验性较强,效果不佳。
# 4. 附录
完整算法及绘图代码:
```python
import scipy
import numpy as np
import matplotlib.pyplot as plt
# 读取音频文件
filename = "./tang1.wav"
sample_rate, sound_array = scipy.io.wavfile.read(filename)
sound_array = sound_array.T[0, :] if sound_array.ndim != 1 else sound_array
sound_array = sound_array / np.max(np.abs(sound_array)) # 归一化
frame_length = int(sample_rate * 0.01)
num_frames = len(sound_array) // frame_length
autocorrelation = np.zeros((num_frames, frame_length))
autocorrelation_of_candidates = np.zeros((num_frames, frame_length))
# 基频阈值为80-400Hz则基音周期即延迟t最小为sample_rate/400最大为sample_rate/80
min_peak_threshold = min(sample_rate // 400, frame_length)
max_peak_threshold = min(sample_rate // 80, frame_length)
for n in range(num_frames):
frame = sound_array[n * frame_length: (n + 1) * frame_length]
autocorrelation[n, :] = scipy.signal.correlate(frame, frame, mode='full')[frame_length - 1:]
# 本应该使用峰值的延迟作为基音周期的候选值,但是发现峰值(局部极大值)并不好判断,同时一帧内的点数不多,因此将阈值内的所有点都作为候选点
# 那么将不在阈值内的自相关系数置为一个非常小的数,从而不让算法选择不在阈值内的基音周期
autocorrelation_of_candidates = autocorrelation
autocorrelation_of_candidates[:, 0:min_peak_threshold] = np.min(autocorrelation) - 10.
autocorrelation_of_candidates[:, max_peak_threshold:] = np.min(autocorrelation) - 10.
x, y = np.meshgrid(np.arange(frame_length), np.arange(num_frames))
x, y, z = x.flatten(), y.flatten(), autocorrelation_of_candidates.flatten()
fig = plt.figure()
ac_3d = fig.add_subplot(111, projection='3d')
sc = ac_3d.scatter(x, y, z, c=z, cmap='plasma')
ac_3d.set_xlabel('Candidates')
ac_3d.set_ylabel('Frame')
ac_3d.set_zlabel('AC Value')
plt.colorbar(sc)
plt.show()
costF = np.zeros((num_frames, frame_length))
path = np.zeros((num_frames, frame_length))
dist = -(autocorrelation_of_candidates - np.min(autocorrelation_of_candidates)) / (np.max(autocorrelation_of_candidates) - np.min(autocorrelation_of_candidates)) * 1e1
costF[0, :] = dist[0, :]
for n in range(num_frames - 1):
for j in range(min_peak_threshold, max_peak_threshold):
# f0 = sample_rate / candidate
costT = np.abs(sample_rate / np.arange(frame_length)[1:] - sample_rate / j)
costT = (costT - np.min(costT)) / (np.max(costT) - np.min(costT)) # 归一化
costG = dist[n + 1, j]
costF[n + 1, j] = costG + np.min(costF[n, 1:] + costT)
path[n + 1, j] = np.argmin(costF[n, 1:] + costT) + 1
l_hat = np.zeros(num_frames, dtype=np.int32)
l_hat[num_frames - 1] = np.argmin(costF[num_frames - 1, 1:]) + 1
for n in range(num_frames - 2, -1, -1):
l_hat[n] = path[n + 1, l_hat[n + 1]]
f0 = sample_rate / l_hat
plt.figure(figsize=(15, 6))
plt.subplot(2, 1, 1)
plt.plot(sound_array)
plt.ylabel("Signal")
plt.subplot(2, 1, 2)
plt.ylim(0, 500)
x = frame_length * np.arange(num_frames)
y = f0
X_Smooth = np.linspace(x.min(), x.max(), 300)
Y_Smooth = scipy.interpolate.make_interp_spline(x, y)(X_Smooth)
plt.plot(X_Smooth, Y_Smooth, color="orange")
plt.plot(x, y, "o")
plt.ylabel("Pitch (Hz)")
plt.show()
```

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

View File

@ -0,0 +1,254 @@
<h1><center>课程作业</center></h1>
<div style="text-align: center;">
<div><span style="display: inline-block; width: 65px; text-align: center;">课程名称</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">计算机语音技术</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">作业名称</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">文献阅读报告</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">学号</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">21281280</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">姓名</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">柯劲帆</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">班级</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">物联网2101班</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">指导老师</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">朱维彬</span></div>
<div><span style="display: inline-block; width: 65px; text-align: center;">修改日期</span><span style="display: inline-block; width: 25px;">:</span><span style="display: inline-block; width: 210px; font-weight: bold; text-align: left;">2023年12月24日</span></div>
</div>
---
> 文献名称:**Attention Is All You Need**
>
> Vaswani A, Shazeer N, Parmar N, et al. Attention is all you need[J]. Advances in neural information processing systems, 2017, 30.
[TOC]
# 1. 文献解读
## 1.1. 引言
引言首先在第一段叙述了当时2017年的深度学习主流架构RNN架构以LSTM和GRU为代表的RNN在多项序列任务中取得了SOTA的结果主要的研究趋势是不断提升递归语言模型和“encoder-decoder”架构的能力上限。
接着第二段叙述了RNN的不足主要是其必须使用串行计算必须依照序列的顺序性并行计算困难。
而注意力机制的应用可以无视序列的先后顺序,捕捉序列间的关系。
因此文章提出一种新架构Transformer完全使用注意力机制来捕捉输入输出序列之间的依赖关系规避RNN的使用。实验证明这种新架构在GPU上训练大幅加快且达到了新的SOTA水平。
总结:
- 论文主题:提出一种新架构
- 解决问题提出能够并行计算的架构替代RNN
- 技术思路:仅使用注意力机制处理输入输出序列
## 1.2. 算法
### 1.2.1 整体架构
<img src="model-arch.png" alt="model-arch" style="zoom: 50%;" />
<p align="center">图1 子编码器架构</p>
上图是Transformer架构的模型网络结构。
整个Transformer模型由$N=6$个这样的Encoder-Decoder对组成如下图所示
<img src="The_transformer_encoder_decoder_stack.png" alt="The_transformer_encoder_decoder_stack" style="zoom: 33%;" />
<p align="center">图2 编码-解码器架构</p>
每个子编码器的架构相同为图1所示但是各个子编码器的模型权重不同同理每个子解码器的架构相同权重不同。
因此,模型的数据处理过程如下:
1. 对输入数据进行文本嵌入;
2. 对输入数据进行位置编码;
3. 将输入数据依次输入$N=6$层子编码器;
4. 将最后一层子编码器的输出分别传入每层子解码器;
5. 对目标数据进行文本嵌入;
6. 对目标数据进行位置编码;
7. 将目标数据依次输入$N=6$层子解码器;
8. 使用全连接层和Softmax层将解码器的输出转换为预测值并输出。
### 1.2.2. 缩放点乘注意力
这一部分包含于多头注意力模块之中。
<img src="Scale-dot-attn.png" alt="Scale-dot-attn" style="zoom: 33%;" />
<p align="center">图3 缩放点乘注意力计算描述</p>
计算公式如下:
$$
\operatorname{Attention}(Q, K, V)=\operatorname{softmax}\left(\frac{Q K^{T}}{\sqrt{d_{k}}}\right) V
$$
其中,输入的$Q$为查询Query向量$K$为关键字Key向量$V$为值Value向量由输入token序列$X$计算得到:
<img src="self-attention-matrix-calculation.png" alt="self-attention-matrix-calculation" style="zoom: 33%;" />
<p align="center">图4 自注意力矩阵计算描述</p>
其中,$W^Q$、$W^K$和$W^V$都是待学习的权重。
接着,$QK^T$表示将查询向量$Q$与关键字向量$K$做内积意在计算两者的相关性。查询与对应token的关键字的相关性越高得到的结果矩阵中的对应值越高受到注意力就会越高。将结果矩阵除以$\sqrt{d_{k}}$避免较高值造成Softmax层的梯度消失送进Softmax层进行概率计算。
得到概率矩阵后,将概率矩阵与值向量$V$相乘作为对应token的值的权重。
最后,将加权的值向量输出。
注意力机制算法的核心思想是对于每个token的键值对查询与一个token的键越相似就对该token的值越关注。
### 1.2.3. 多头注意力
这部分对应于图1中的`Multi-Head Attention`
<img src="multi-head.png" alt="multi-head" style="zoom: 33%;" />
<p align="center">图5 多头注意力计算描述</p>
计算公式如下:
$$
\begin{align}
\begin{aligned}
\operatorname{MultiHead}(Q, K, V) & = \operatorname{Concat}\left(\operatorname{head}_{1}, \ldots, \operatorname{head}_{\mathrm{h}}\right) W^{O} \\
\text { where head } & = \operatorname{Attention}\left(Q W_{i}^{Q}, K W_{i}^{K}, V W_{i}^{V}\right)
\end{aligned}
\end{align}
$$
多头注意力实际上是多个自注意力的叠加,类似卷积神经网络中多个通道特征,这样可以有效学习到多个不同的特征。
将查询向量$Q$、关键字向量$K$和值向量$V$复制$h=8$份($h$即为“多头”的头数),分别放入多个自注意力计算模块中计算特征。最后将得到的特征堆叠,送入全连接层融合。
### 1.2.4. 应用注意力机制
<img src="transformer_resideual_layer_norm_2.png" alt="transformer_resideual_layer_norm_2" style="zoom: 33%;" />
<p align="center">图6 子编码器计算流程</p>
在编码器中,每一个子编码器都由以下计算流程依次组成:
1. 输入token序列$X$进行多头注意力计算;
2. 将$h$个自注意力模块计算结果使用全连接层整合;
3. 与原输入序列$X$相加,构成残差网络,并进行层归一化;
4. 将结果送入前馈网络;
5. 进行残差计算,随后归一化,最后输出。
<img src="transformer_resideual_layer_norm_3.png" alt="transformer_resideual_layer_norm_3" style="zoom: 33%;" />
<p align="center">图7 编码解码器计算流程</p>
在解码器中,每一个子解码器都由以下计算流程依次组成:
1. 对目标token序列进行掩码处理
2. 送入多头注意力模块计算;
3. 与输入的目标序列求和计算残差并归一化;
4. 将编码器输出结果作为查询向量$Q$,从目标序列计算出关键字向量$K$和值向量$V$,输入自注意力模块计算;
5. 与输入序列求和计算残差并归一化;
6. 将结果送入前馈网络;
7. 与输入序列求和计算残差并归一化,最后输出。
### 1.2.5. 前馈网络
计算公式如下:
$$
FFN\left(x\right) = max\left(0, xW_1+b_1\right)W_2 + b_2
$$
两层全连接层之间使用RELU作为激活层。
### 1.2.6. 掩码多头注意力
由于Transformer架构支持并行计算当预测${Output}_t$需要在时,不能向模型提供${Output}_{t+1}$及之后的子序列,因此要对目标序列输入进行掩码。
比如一组数据为
```txt
{
Input: "i love computer speech technology",
Target: "我爱计算机语音技术"
}
```
希望预测Target[4]“机”时只能向模型提供Target[0:4]“我爱计算”,而不能输入后面的序列,因此需要将其遮掩。
遮掩方法是让注意力公式的Softmax的输入为$-\infty$那么得到的后面的token的注意力权重就几乎为0达到遮住后面的token的效果。
即希望预测Target[4]“机”时Softmax的输入相当于["我", "爱", "计", "算", "$-\infty$", "$-\infty$", "$-\infty$", "$-\infty$", "$-\infty$"],其中文字代表其对应的计算得到的注意力权重。
### 1.2.7. 位置编码
计算公式为:
$$
\begin{aligned}
P E_{(\text {pos }, 2 i)} & =\sin \left(\operatorname{pos} / 10000^{2 i / d_{\text {model }}}\right) \\
P E_{(\text {pos }, 2 i+1)} & =\cos \left(\operatorname{pos} / 10000^{2 i / d_{\text {model }}}\right)
\end{aligned}
$$
其中$pos$为token在序列中的下标$2i$或$2i+1$为词向量的维度序号。即词向量维度为偶数时使用正弦函数,为奇数时使用余弦函数。
该函数满足以下性质:
- 对于一个词嵌入向量的不同元素,编码各不相同;
- 对于向量的同一个维度处,不同$pos$的编码不同。且$pos$间满足相对关系:
$$
\left\{\begin{array}{l}
P E(\text { pos }+k, 2 i)=P E(\text { pos }, 2 i) \times P E(k, 2 i+1)+P E(\text { pos, } 2 i+1) \times P E(k, 2 i) \\
P E(\text { pos }+k, 2 i+1)=P E(\text { pos }, 2 i+1) \times P E(k, 2 i+1)-P E(\text { pos }, 2 i) \times P E(k, 2 i)
\end{array}\right.
$$
从实际意义上看即例如Target[4]“机”的位置编码可以被Target[1]“爱”和Target[3]“算”的位置编码线性表示。
## 1.3. 结论
### 1.3.1. 实验结果
研究测试了“英语-德语”和“英语-法语”两项翻译任务。使用论文的默认模型配置在8张P100上只需12小时就能把模型训练完。
研究使用了Adam优化器并对学习率调度有一定的优化。模型有两种正则化方式
1. 每个子层后面有Dropout丢弃概率0.1
2. 标签平滑。
<img src="result.png" alt="result" style="zoom: 25%;" />
<p align="center">图8 翻译任务实验结果</p>
实验表明Transformer在翻译任务上胜过了所有其他模型且训练时间大幅缩短。
论文同样展示了不同配置下Transformer的消融实验结果。
<img src="result2.png" alt="result2" style="zoom: 25%;" />
<p align="center">图9 消融实验结果</p>
实验A表明计算量不变的前提下需要谨慎地调节$h$和$d_k$、$d_v$的比例太大太小都不好。这些实验也说明多头注意力比单头是要好的。实验B表明$d_k$增加可以提升模型性能。作者认为这说明计算key、value相关性是比较困难的如果用更精巧的计算方式来代替点乘可能可以提升性能。实验C, D表明大模型是更优的且dropout是必要的。如正文所写实验E探究了可学习的位置编码。可学习的位置编码的效果和三角函数几乎一致。
### 1.3.2. 研究结论
Transformer这一仅由注意力机制构成的模型。Transformer的效果非常出色不仅训练速度快了还在两项翻译任务上胜过其他模型。
Transformer在未来还可能被应用到图像、音频或视频等的处理任务中。
## 1.4. 个人见解
在接触过的研究中我使用过很多基于Transformer架构的模型。
在图片Caption任务中我将Transformer与CNN和RNN结合的编码-解码器进行效果比较后者的效果非常差而Transformer可以达到较好的效果。
另外基于Transformer架构的各类大模型可以达到很好的效果其中我体验过商用的ChatGPT也复现过开源的[Llama2](https://ai.meta.com/research/publications/llama-2-open-foundation-and-fine-tuned-chat-models/)、[LLaVA](https://arxiv.org/abs/2304.08485)等VQA任务模型使用过[RoBERTa](https://arxiv.org/abs/1907.11692)等BERT-base模型完成token分类任务使用[ViT](https://arxiv.org/abs/2010.11929)为[CLIP](https://proceedings.mlr.press/v139/radford21a)等图像分类任务模型进行图像特征提取等。
由于学识不足个人无法独立给出对Transformer架构本身的评价。因此本人查阅了近年来对Transformer进行改进或替代的研究搜集到如下几点问题或改进方法
- Transformer模型中自注意力机制的计算量会随着上下文长度的增加呈平方级增长计算效率非常低。最近一项研究[Mamba: Linear-Time Sequence Modeling with Selective State Spaces](https://arxiv.org/abs/2312.00752)针对长文本有更好的效果;
- [SIMPLIFYING TRANSFORMER BLOCKS](https://arxiv.org/abs/2311.01906)发现可以移除一些Transformer模块的部分比如残差连接、归一化层和值参数以及MLP序列化子块有利于并行布局以简化类似 GPT 的解码器架构以及编码器式BERT模型。
# 2. 参考和引用资料
- https://zhuanlan.zhihu.com/p/569527564
- https://jalammar.github.io/illustrated-transformer/