搞懂语音去噪

文章目录

  • 1 概述
  • 2 传统语音去噪
    • 2.1 谱减法
    • 2.2 维纳滤波法
  • 3 深度语音去噪
  • 参考资料

1 概述

语音去噪(noise reduction)又被称为语音增强(speech enhancement),主要是针对于有人声的音频进行处理,目的是去除那些背景噪声,增强音频中人声的可懂性(intelligibility)。其应用范围很广,可以用于人与人之间的语音通讯,也可以用于很多语音任务的预处理,比如Automatic speech recognition。

这里的噪声通常被分为两大类,stationary和non-stationary。

stationary noise是指不随着时间发生变化变化的噪声,比如菜场的嘈杂声,电台的杂讯声等等

non-stationary noise是指随时间发生变化的噪声,比如说话时背后突然经过一辆汽车,又比如突然响起的警报声等等

举个实际应用中去噪的例子,我们的手机一般会为我们的通话自动做降噪处理,它会在离说话人嘴巴较近的地方装一个声音接收器,又会在离说话人嘴巴较远的地方装一个声音接收器,并认为后者接收到的声音基本就是noise,然后在这个前提下对人说的话进行去噪。有这样的设备的帮助,去除non-stationary noise会更方便一些,但很多情况下,我们拿到的就只有一段有噪声的语音。

2 传统语音去噪

2.1 谱减法

谱减法应该是最早被用于语音去噪的方法,它的思想非常简单,就是通过估计出噪声,并在频域里将其幅值剪掉,再还原,就结束了。为了表示方便,我们先假设纯净的声音为x(n)x(n)x(n),原始声音为y(n)y(n)y(n),噪声为e(x)e(x)e(x),就有

y(n)=x(n)+e(n)(2-1)y(n) = x(n) + e(n) \tag{2-1} y(n)=x(n)+e(n)(2-1)

这里只有y(n)y(n)y(n)是我们有的,其他x(n)x(n)x(n)e(n)e(n)e(n)都还不知道,目的是把x(n)x(n)x(n)给求出来。

noisereduce中的stationary的方法就是用谱减法去做的,效果还是不错的,不过也只能应对于stationary noise。

我们按谱减法步骤来说明一下整个过程。

(1)截取头部一小段语音作为噪声
e(m)=y(n)[:m]e(m) = y(n)[:m] e(m)=y(n)[:m]

其中eee表示噪声信号,yyy表示原始信号,mmmnnn表示sample的数量。

我们认为stationary noise是一直存在于背景当中的声音,而人声一般在开头的几十毫秒是没有的,所以就默认取前面一小段作为噪声。不过当无法确定噪声的时候,把整段声音都作为噪声也是可以的,noisereduce就是这么做的。

(2)分别计算原始音频和噪声的STFT,Y(ω)Y(\omega)Y(ω)E(ω)E(\omega)E(ω)
(3)根据噪声的频谱幅值,对原始音频的频谱幅值进行谱减。

最粗暴的做法是直接对每个频率上的∣E(ω)∣|E(\omega)|E(ω)沿时间轴取均值得到∣E(ω)∣mean|E(\omega)|_{mean}E(ω)mean,然后再把原始音频每个时间点每个频率上的幅值减去对应的均值,并把相减后幅值小于0的置0,作为对纯净音频的频谱幅值估计∣X^(ω)∣|\hat{X}(\omega)|X^(ω)。有的地方也会用能量谱∣X^(ω)∣2|\hat{X}(\omega)|^2X^(ω)2,这里只是为了说明大意,不管这些细节了。

∣X^(ω)∣={∣Y(ω)∣−∣E(ω)∣mean,if∣Y(ω)∣−∣E(ω)∣mean>00,otherwise(2-2)|\hat{X}(\omega)| = \begin{cases} |Y(\omega)| - |E(\omega)|_{mean}, & if \ |Y(\omega)| - |E(\omega)|_{mean} > 0 \\ 0, &otherwise \end{cases} \tag{2-2} X^(ω)={Y(ω)E(ω)mean,0,if Y(ω)E(ω)mean>0otherwise(2-2)

这样做不好的地方就是会有很多坑坑洼洼的噪声频率残留,这个现象也被称为是音乐噪声。实际操作过会发现这种方法减了和没减差不多。因此有人提出了过减法,就是宁可错杀一千不能放过一个的做法。

∣X^(ω)∣={∣Y(ω)∣−α∣E(ω)∣mean,if∣Y(ω)∣>(α+β)∣E(ω)∣meanβ∣E(ω)∣,otherwise(2-3)|\hat{X}(\omega)| = \begin{cases} |Y(\omega)| - \alpha |E(\omega)|_{mean}, & if \ |Y(\omega)| > (\alpha + \beta)|E(\omega)|_{mean}\\ \beta |E(\omega)|, &otherwise \end{cases} \tag{2-3} X^(ω)={Y(ω)αE(ω)mean,βE(ω),if Y(ω)>(α+β)E(ω)meanotherwise(2-3)

其中,α∈[0,+∞]\alpha \in [0, +\infin]α[0,+]是过减因子,β∈[0,1]\beta \in [0, 1]β[0,1]是谱下限参数,用来控制残留多少的噪声。这样减出来噪声会明显少了很多,但声音也会随着α\alphaα的增大而逐渐失真。

noisereduce中的具体实现略有不同,它过减用∣E(ω)∣|E(\omega)|E(ω)的方差来控制,一般是1.5倍或者1.0倍的方差。代码片段如下所示

self.mean_freq_noise = np.mean(noise_stft_db, axis=1)
self.std_freq_noise = np.std(noise_stft_db, axis=1)
self.noise_thresh = self.mean_freq_noise + self.std_freq_noise * self.n_std_thresh_stationary

小于noise_thresh的幅值会置0,其余的保留。n_std_thresh_stationary为0时,就是没有过减的式(2−2)(2-2)(22)

(4)对∣X^(ω)∣|\hat{X}(\omega)|X^(ω)做平滑处理,使得声音失真没那么严重。

noisereduce中使用的scipy.signal.fftconvolve来实现这一过程。

(5)结合原始音频的相位,还原谱减后的音频。这就是个反向STFT的过程。

建议看一下noisereduce的代码,还是比较容易理解的。

2.2 维纳滤波法

维纳滤波法(wiener filter)也是一个比较经典的传统做法,它的本质是估计出一个线性滤波器,也就是一个向量,这个滤波器会对不同的频段进行不同程度的抑制,其保真效果会比谱减法要好一些。

我们这里不会讲详细的推导过程,只讲其大致思想。因为这么大功夫推导出来,还是有很多不能解决的问题,还不如深度学习train一发。想看详细推导了可以去看知乎的卡尔曼滤波器详解——从零开始(3) Kalman Filter from Zero这篇,于泓-语音增强-维纳滤波这个视频讲的更偏向于应用,都很棒。

还有就是这里讲的是smoothing的问题,即根据未来的信号,过去的信号以及现在的信号来推测出现在的干净信号。除此之外,还有prediction和filtering的问题,prediction是指根据过去的和现在的信号,预测未来的干净信号;filtering的问题是指根据过去和现在的信号,推测现在的干净信号。所以这里讲的方法没法应用于实时语音去噪,只能在拿到整段信号之后,对这段信号进行去噪。

维纳滤波器的设计准则为使得干净信号x(n)x(n)x(n)和估计的干净信号x^(n)\hat{x}(n)x^(n)之间的差值越小越好,即计算一个最小均方差

MSE(x^)=E(x^(n)−x(n))2(2-4)MSE(\hat{x}) = E(\hat{x}(n) - x(n))^2 \tag{2-4} MSE(x^)=E(x^(n)x(n))2(2-4)

这里的x^(n)\hat{x}(n)x^(n)是估计的干净信号,x(n)x(n)x(n)是真实的干净信号。

我们假设设计出来的滤波器为h(n)h(n)h(n),则我们有

x^(n)=h(n)∗y(n)(2-5)\hat{x}(n) = h(n)*y(n) \tag{2-5} x^(n)=h(n)y(n)(2-5)

这里的y(n)y(n)y(n)是原始信号,∗*表示卷积。时域的卷积就是频域的乘积。就有

X^(ω)=H(ω)Y(ω)(2-6)\hat{X}(\omega) = H(\omega)Y(\omega) \tag{2-6} X^(ω)=H(ω)Y(ω)(2-6)

我们用(2−4)(2-4)(24)来计算这里的H(ω)H(\omega)H(ω),这里省略去一大波的推导过程,最终有

H(ωk)=Pxx(ωk)Pxx(ωk)+Pnn(ωk)(2-7)H(\omega_k) = \frac{P_{xx}(\omega_k)}{P_{xx}(\omega_k) + P_{nn}(\omega_k)} \tag{2-7} H(ωk)=Pxx(ωk)+Pnn(ωk)Pxx(ωk)(2-7)

其中,Pxx(ωk)=E[∣X(ωk)∣2]P_{xx}(\omega_k) = E[|X(\omega_k)|^2]Pxx(ωk)=E[X(ωk)2]Pnn(ωk)=E[∣N(ωk)∣2]P_{nn}(\omega_k) = E[|N(\omega_k)|^2]Pnn(ωk)=E[N(ωk)2],这里为了避免符号混淆,把噪声的频域用N(ωk)N(\omega_k)N(ωk)来表示的。

(2−7)(2-7)(27)也可以表示为

H(ωk)=ξkξk+1(2-8)H(\omega_k) = \frac{\xi_k}{\xi_k + 1} \tag{2-8} H(ωk)=ξk+1ξk(2-8)

其中,ξk=Pxx(ωk)Pnn(ωk)\xi_k=\frac{P_{xx}(\omega_k)}{P_{nn}(\omega_k)}ξk=Pnn(ωk)Pxx(ωk)为先验信噪比,就是干净信号的能量谱和噪声能量谱的比例。

(2−8)(2-8)(28)可以看出,当噪声占比比较小时,H(ωk)H(\omega_k)H(ωk)就比较大,表示允许干净信号通过;当噪声占比比较大时,H(ωk)H(\omega_k)H(ωk)就比较小,表示抑制噪声信号通过。

(2−8)(2-8)(28)有一个变种的泛化形式

H(ωk)=(ξkξk+α)β(2-9)H(\omega_k) = (\frac{\xi_k}{\xi_k + \alpha})^{\beta} \tag{2-9} H(ωk)=(ξk+αξk)β(2-9)

这里的α\alphaαβ\betaβ都是可以设置的参数,当α=1\alpha = 1α=1并且β=1\beta=1β=1时,式(2−9)(2-9)(29)就变成了式(2−8)(2-8)(28)。不同的α\alphaαβ\betaβ的值可以控制对噪声的抑制程度,当我们事先知道噪声大概在哪个频段的时候,就可以对不同的频段设置不同的α\alphaαβ\betaβ

实际应用时我们并没有干净信号,也没有噪声信号,所以似乎没法算H(ωk)H(\omega_k)H(ωk)。这就需要我们先去估计一个噪声信号和干净的信号了。估计的方法可以用2.1中的谱减法,也就是说当只有含噪声的原始信号时,维纳滤波就是在谱减法的基础上再进行了一次估计。

比如我们有一个长度为(23410,)(23410,)(23410,)的信号,经过谱减法之后得到了一个(23296,)(23296,)(23296,)的干净信号和噪声信号。由于短时傅里叶的原因,信号长度会变短一些,这个不影响。我们拿谱减估计的干净信号和噪声信号去计算滤波器。干净信号和噪声信号经过STFT之后都变成了(129,183)(129, 183)(129,183)的信号,其中129表示有129个频段,183时间维度上按窗口分割的分段数量。H(ωk)H(\omega_k)H(ωk)计算出来是一个(129,1)(129, 1)(129,1)的向量,即对每个频段的抑制程度,然后整条信号过这个滤波器之后,做ISTFT还原。示例代码可见test_wiener_2.py。

从这里也不难看出,对整条信号使用的滤波器参数是固定的。这也使得该方法无法搞定non-stationary noise。

3 深度语音去噪

前人想了这么多用公式推导而来的去噪方法,都不能很好地搞定non-stationary noise,还不如深度学习train一发。深度学习的效果是真的好,而且速度都比传统的方法快,只要有数据就行,数据驱动才是王道啊。

这里要讲的是facebook出品的机遇DEMUCS的denoiser。DEMUCS之前是用于音频分轨(source separation)的,去噪的本质其实也就是把人声轨给分离出来,与其说是去噪,不如说是提取人声更为合理一些。当然,这个都是由数据控制的。其目的是用神经网络构建一个函数fff使得式(2−1)(2-1)(21)中的

x(n)=f(y(n))(3-1)x(n) = f(y(n)) \tag{3-1} x(n)=f(y(n))(3-1)

denoiser的模型架构非常简明易懂,也非常轻量,可以用于实时的语音去噪,其结构示意图如下图3-1所示。
demucs structure

图3-1 DEMUCS网络结构示意图

整个结构就是一个U-net的结构,输入和输出都直接是声音信号,Encoder和Decoder都分别有LLL层,每一层都是由一个conv1d+relu+conv1d+glu组成的,其示意图如下图3-2所示。

encoder and decoder

图3-2 encoder和decoder结构示意图

其中,glu中的conv1d是一个kernel_size=1的卷积,主要目的是改变channel的数量,同时也可以在channel之间做特征的融合。encoderiencoder_iencoderi的输入只有上一个encoderi−1encoder_{i-1}encoderi1的输出,decoderidecoder_idecoderi的输入除了上一个decoderi+1decoder_{i+1}decoderi+1的输入之外,还有encoderiencoder_iencoderi的输出。decoderidecoder_idecoderi利用encoderiencoder_iencoderi这样的操作也被称作skip connection。

encoderLencoder_LencoderL的最终输出会经过一个LSTM之后再进入decoderLdecoder_LdecoderL。记encoderLencoder_LencoderL的输出为zzzdecoderLdecoder_LdecoderL的输入特征为z^\hat{z}z^,则有

z^=LSTM(z)+z(3-2)\hat{z} = LSTM(z) + z \tag{3-2} z^=LSTM(z)+z(3-2)

网络的loss由两部分组成,分别是L1 loss和多尺度的STFT loss组成。前者保证输出信号相近,后者保证组成该输出信号的频率相近。

L1 loss表示了目标信号和模型输出信号之间的差值,表示为

Lwaveform=1T∣∣x−x^∣∣1(3-3)L_{waveform} = \frac{1}{T} ||\bold{x} - \bold{\hat{x}}||_1 \tag{3-3} Lwaveform=T1xx^1(3-3)

其中,x\bold{x}x是干净的目标信号,x^\bold{\hat{x}}x^是模型输出的信号,TTT为采样点的数量。

不过在实际的代码实现中,这个loss可以是L1 loss,也可以是L2 loss,还可以是smooth L1 loss。

多尺度的STFT loss是指用不同的fft bins,hop sizes和window lengths的到的各个STFT下的loss,这也是为了让模型不过拟合于某一种参数下的STFT变换。

Lstft=1T∑i=1MLstft(i)(x,x^)(3-4)L_{stft} = \frac{1}{T} \sum_{i=1}^M L_{stft}^{(i)}(\bold{x}, \bold{\hat{x}}) \tag{3-4} Lstft=T1i=1MLstft(i)(x,x^)(3-4)

其中,每个Lstft(i)(x,x^)L_{stft}^{(i)}(x, \hat{x})Lstft(i)(x,x^)由spectral convergence (sc) loss和magnitude loss组成。

Lstft(i)(x,x^)=Lsc(i)(x,x^)+Lmag(i)(x,x^)(3-5)L_{stft}^{(i)}(\bold{x}, \bold{\hat{x}}) = L_{sc}^{(i)}(\bold{x}, \bold{\hat{x}}) + L_{mag}^{(i)}(\bold{x}, \bold{\hat{x}}) \tag{3-5} Lstft(i)(x,x^)=Lsc(i)(x,x^)+Lmag(i)(x,x^)(3-5)

spectral convergence (sc) loss为

Lsc(i)(x,x^)=∣∣∣STFT(i)(x)∣−∣STFT(i)(x^)∣∣∣F∣∣∣STFT(i)(x)∣∣∣F(3-6)L_{sc}^{(i)}(\bold{x}, \bold{\hat{x}}) = \frac{|| |STFT^{(i)}(\bold{x})| - |STFT^{(i)}(\bold{\hat{x}})| ||_F}{|| |STFT^{(i)}(\bold{x})| ||_F} \tag{3-6} Lsc(i)(x,x^)=STFT(i)(x)FSTFT(i)(x)STFT(i)(x^)F(3-6)

magnitude loss为
Lmag(i)(x,x^)=∣∣log∣STFT(i)(x)∣−log∣STFT(i)(x^)∣∣∣1L_{mag}^{(i)}(\bold{x}, \bold{\hat{x}}) = || log|STFT^{(i)}(\bold{x})| - log|STFT^{(i)}(\bold{}\hat{x})| ||_1 Lmag(i)(x,x^)=logSTFT(i)(x)logSTFT(i)(x^)1

没错,效果这么好的一个网络就是这么简单明了。训练的时候当然也用到了一些数据增强等等的方法,这里就不说了,想了深入了解的可以看参考资料中的文献和代码。

参考资料

[1] 雷霄骅-谱减法语音降噪原理
[2] 于泓-语音增强-谱减法
[3] https://github.com/timsainb/noisereduce
[4] 于泓-语音增强-维纳滤波
[5] https://github.com/facebookresearch/denoiser
[6] Real Time Speech Enhancement in the Waveform Domain
[7] 卡尔曼滤波器详解——从零开始(3) Kalman Filter from Zero

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/470564.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

软件架构设计案例_透过现象看本质:常见的前端架构风格和案例

所谓软件架构风格,是指描述某个特定应用领域中系统组织方式的惯用模式。架构风格定义一个词汇表和一组约束,词汇表中包含一些组件及连接器,约束则指出系统如何将构建和连接器组合起来。软件架构风格反映了领域中众多系统所共有的结构和语义特…

十九、PHP框架Laravel学习笔记——批量赋值和软删除

一.批量赋值 上一节增删改中,新增中我们发现需要进行批量赋值的许可;一般情况下,是为了防止提交过来的字段在部分场景中不需要或不能;所以,我们需要通过黑白名单机制进行过滤掉必要的字段; //通…

speech production model

文章目录1 概述2 source model3 filter model4 小结参考资料1 概述 本文的目的是为了厘清在speech production model中source model和filter model所扮演的角色,不涉及具体公式的推导或者模型的建立,只是为了把这两个model在干什么事情说明白。文中用到…

二十、PHP框架Laravel学习笔记——模型的作用域

一.本地作用域 很多情况下,我们在数据查找时有一部分条件会被重复且大量使用;而这个条件,可能只是在这个模型对应的数据表使用,别的表并不使用;那么这种情况,可以使用本地作用域的方式&#xf…

论文阅读 - Joint Beat and Downbeat Tracking with Recurrent Neural Networks

文章目录1 概述2 信号预处理3 分类神经网络4 动态贝叶斯网络(HMM)4.1 原始的bar pointer model4.2 原始的bar pointer model的缺点4.3 改进后的模型5 预测参考资料1 概述 最近在做音乐卡点相关的项目,需要对音乐的基本特征进行理解&#xff…

二十一、PHP框架Laravel学习笔记——模型的访问器和修改器

一.访问器 访问器:就是在获取数据列表时,拦截属性并对属性进行修改的过程;比如,我们在输出性别时,在性别左右加上括号,或给邮件转换为大写; //访问器,前固定 get&#…

python将txt转json_Python控制乐高EV3,以及VSCODE环境配置

乐高EV3的可扩展性很强,但如何用pc连接ev3,并用python代码来控制EV3,资料太少了,试着做了一次,记录在这里。需要的硬/软件硬件准备一、乐高EV3二、PC,win10系统三、TF卡(我用的是64G的&#xff…

移动文件读/写指针----lseek

头文件&#xff1a;#include<sys/types.h>、#include<unistd.h> 函数原型&#xff1a;off_t lseek(int fildes,off_t offset,int whence) 参数说明&#xff1a;fildes:文件描述符 offset:偏移量&#xff0c;正数表示正向偏移&#xff0c;负数表示负向偏移。 whence…

Yolo系列知识点梳理(Yolov1-v5)

文章目录1 概述2 Yolo系列模型2.1 基石 - Yolov12.1.1 Yolov1的网络结构2.1.2 Yolov1的feature map2.1.3 Yolov1的训练2.1.4 Yolov1的预测2.1.5 Yolov1小结2.2 Yolo9000 - Yolov22.2.1 Better2.2.1.1 引入了Batch normalization2.2.1.2 高分辨率的分类器2.2.1.3 加入了anchor机…

二十二、PHP框架Laravel学习笔记——集合的使用

一&#xff0e;创建集合 什么是集合&#xff1f;即&#xff1a;它是一种更具读取性和处理能力的数组封装&#xff1b;比如&#xff0c;我们从数据库得到的数据列表&#xff0c;它就是一种集合&#xff1b;数据集合&#xff0c;提供了大量的方法方便我们进行各种操作&#xff1…

二十三、PHP框架Laravel学习笔记——集合的常用方法

一&#xff0e;常用方法 all()方法&#xff0c;转换为属性形式输出&#xff0c;使用 dd 方法看类型&#xff1b; $collection collect([1, 2, 2, 3, 4, 4, 4]); dd($collection->all()); PS&#xff1a;$collection->dd()方法可以以 dd()模式输出&#xff0c;还有 du…

mac看图软件哪个好用_细数Mac上那些好用且免费的软件(三)

许多朋友购买了Mac电脑之后发现很多软件都没有&#xff0c;而且苹果商店好多软件都是收费的。那有没有免费的软件也能满足日常的需求呢&#xff1f;macw小编就为大家推荐一些免费且超级好用的软件&#xff0c;赶快来看看有没有你需要的吧&#xff01;细数Mac上那些好用且免费的…

图像表格实线和虚线检测

文章目录1 背景简述2 camelot中的方法2.1 二值化2.2 腐蚀膨胀2.3 轮廓检测2.4 结果展示3 基于霍夫直线检测的方法3.1 霍夫直线检测原理3.2 概率霍夫直线检测3.3 霍夫直线应用参考资料1 背景简述 图像中的表格结构化是一个比较热门的话题&#xff0c;其输入是一张图片&#xff…

二十四、PHP框架Laravel学习笔记——模型的数据集合

一&#xff0e;数据集合 数据集合&#xff0c;就是已经将模型方法 get()获取到的数据再进行处理&#xff1b;比如&#xff1a;map()方法&#xff0c;通过它可以实现类似访问器一样对字段进行处理的效果&#xff1b; $users User::get(); //使用集合方法 map 可以对输出的字…

论文阅读 - AUTOVC: Zero-Shot Voice Style Transfer with Only Autoencoder Loss

文章目录1 概述2 模型架构3 模块解析3.1 获取梅尔频谱3.2 speaker encoder3.3 AutoVC3.4 Vocoder4 关键部分参考资料1 概述 voice conversion这个任务的目标是输入两个音频&#xff0c;其输入是两段音频&#xff0c;一段音频称为content_audio&#xff0c;另一段称为speaker_a…

二十五、PHP框架Laravel学习笔记——模型的一对一关联

一&#xff0e;关联概念 关联模型&#xff0c;即&#xff1a;两张或以上的表进行一定规则的绑定关联&#xff1b;比如&#xff1a;一个学生(学生表)对应一张个人信息卡(信息表)&#xff0c;这种就是一对一&#xff1b;再比如&#xff1a;一篇博文(帖子表)对应多个评论(评论表)…

小工具:基于颜色的视频和图片切割

文章目录1 前言2 方案简述3 效果1 前言 最近做一个短视频相关的项目的时候&#xff0c;发现输入的视频有很多是有黑边的&#xff0c;有些可能是白边或者其他颜色的边。这对下游的模型处理有很大的影响。于是就写了一个自动判断填充边的颜色&#xff0c;并根据该颜色自动切割视…

二十六、PHP框架Laravel学习笔记——模型的一对多关联

二&#xff0e;一对多关联 一对多关联&#xff0c;本质上使用方法和一对一关联类似&#xff0c;内部实现略有不同&#xff1b; 创建另一个模型&#xff1a;book.php&#xff0c;我们看下这个表数据&#xff1b; PS&#xff1a;这里 user_id19 有三个&#xff0c;也就是蜡笔小…

论文阅读 - An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale

文章目录1 概述2 方法简述2.1 encoder之前2.2 encoder之后3 实验结果参考资料1 概述 这篇论文是一篇将tranformer引入到图像领域的里程碑式的文章。因为这是第一次在处理图像时&#xff0c;将所有的卷积模块统统抛弃&#xff0c;只使用attention。并且实验证明了只用attention…

python五子棋人机对战_Python:游戏:五子棋之人机对战

原标题&#xff1a;Python&#xff1a;游戏&#xff1a;五子棋之人机对战 开端 画棋盘 首先肯定是要画出棋盘来&#xff0c;用 pygame 画出一个 19 19 或 15 15 的棋盘并不是什么难事&#xff0c;这在之前的文章中已经多次用到&#xff0c;就不赘述了。 画棋子 需要说一下的是…