语音信号处理——常识与基本概念
数字信号
连续时间信号:在连续时间范围内定义的信号,信号的幅度可以是连续的(模拟信号),也可以是离散的
离散时间信号:时间为离散变量的信号,即独立变量时间被量化了,而幅度仍是连续变化的
数字信号:时间离散并且幅度量化的信号,如果是二进制量化,只有1,0两种模式的信号。四进制数字信号只有四种取值,以此类推。数字信号幅度只取几个量化的值代替区间。
从模拟信号到数字信号
采样
采样率:每秒从连续信号中提取并组成离散信号的采样个数。用Hz表示,采样频率的倒数是相邻采样点之间的时间间隔。
采样定理(奈奎斯特采样定理):只有采样频率高于声音信号最高频率的两倍时,才能把数字信号表示的声音还原成为原来的声音。所以语音信号最大能够达到的最高频率等于采样率的一半。
举例:如果有两个频率分别为20 Hz的语音和一个20 KHz的语音,我们以44.1KHz的采样率对语音进行采样。(采样频率和语音频率单位虽然都是Hz,但是不同,注意区别)
结果:20Hz语音每次振动被采样了
次; 20KHz语音每次振动被采样了
次; 所以在相同的采样率下,记录低频的信息远远比高频的详细。
混叠(欠采样):当采样频率小于最大截止频率两倍(奈奎斯特频率)的时候就会发生信号重叠,大白话就是我的信号是8kHz的频率,你却没有用16kHz的采样频率来采样,导致了我产生了混叠。
为了避免混叠现象,通常采用两种措施:
- 提高语音采样率: 到信号最高频率的两倍以上;
- 频率高于采样率一半的信号:通过抗混叠滤波器(低通滤波器) 滤除频率高于采样率一半的信号
下采样:以
上采样:以
量化
采样后的离散信号在振幅维度依然是连续的,需要经过量化才能变成数字信号,数字信号只取几个量化值代替离散信号的振幅区间。转变成数字信号后的语音信号,降低了对硬件传输和存储的要求,便于用到复杂的算法中进行计算和分析语音声学特性,并且还提高了在传输过程中的抗干扰能力、可靠性和保密性。但是,量化值和离散值之间存在一定的量化失真,会对语音信号产生类似于白噪声的干扰,在听觉表现上会出现“沙沙”声。
由于语音信号的频率不一,且量化位数和语音信噪比直接挂钩,在数字电话系统中,通常会使用“A-law”[49]或“u-law”[50,51]量化编码机制,其中“A-law”主要在欧洲使用,“u-law”主要在北美和日本使用,在低频部分语音变化小,使用较大的量化间隔,在高频部分语音变化大,使用较小的量化间隔,当语音量化分级越多时,量化失真越小。
编码
多进制量化语音信号需要在电子设备上进行传输、存储和计算,要先进行二进制编码,使用位深表示每个采样点中的信息比特数,通常麦克风的量化位深为8 比特、16 比特、24 比特和32 比特。通常使用8 比特量化位深,因此通常手机通话时麦克风采集语音的量化位深也为8 比特。
数字和模拟频率
人声的频谱范围是20Hz~20kHz,工程上信号分为 数字频率
其中
模拟频率还有一个概念模拟角频率
弧度转角度:
角度转弧度:
带宽(也称频带):语音的频率范围,最高频率等于采样频率的一半。
语谱图和频谱图
语音波形图
波形图表示语音信号的响度随时间变化的规律,横坐标表示时间,纵坐标表示声音响度,我们可以从时域波形图中观察语音信号随时间变化的过程以及语音能量的起伏

import matplotlib.pyplot as plt import numpy as np import librosa plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签 plt.rcParams['axes.unicode_minus']=False #用来正常显示符号 fs = 16000 audio, _ = librosa.load("./p225_001.wav", sr=fs) audio = audio[5920:15000] time = np.arange(0, len(audio)) * (1.0 / fs) plt.title("语音信号", fontsize=14) plt.plot(time, audio, color='C0') plt.xlabel("时间/s", fontsize=14) plt.ylabel("振幅", fontsize=14) plt.tight_layout() plt.show()
频谱图和相位图
频谱图(也称为频响图)表示语音信号的幅度随频率变化的规律,信号频率与能量的关系用频谱表示,频谱图的横轴为频率,纵轴为频率的强度(功率),以dB为单位

# Author:凌逆战 # -*- coding:utf-8 -*- import matplotlib.pyplot as plt import numpy as np import librosa plt.rcParams['font.sans-serif'] = ['SimHei'] # 指定默认字体 plt.rcParams['axes.unicode_minus'] = False # 用来正常显示符号 fs = 16000 audio, _ = librosa.load("../wav_data/p225_001.wav", sr=fs) time = np.arange(0, len(audio)) * (1.0 / fs) plt.subplot(2, 2, 1) plt.title("(a) 幅度谱图") plt.magnitude_spectrum(audio, Fs=fs, color='C1') plt.xlabel("频率", fontsize=13) plt.ylabel("幅度 (能量)", fontsize=13) plt.subplot(2, 2, 2) plt.title("(b) 对数幅度谱图") plt.magnitude_spectrum(audio, Fs=fs, scale='dB', color='C1') plt.xlabel("频率", fontsize=13) plt.ylabel("幅度 (dB)", fontsize=13) plt.subplot(2, 2, 3) plt.title("(c) 相位谱图") plt.phase_spectrum(audio, Fs=fs, color='C2') plt.xlabel("频率", fontsize=13) plt.ylabel("相位 (弧度)", fontsize=13) plt.subplot(2, 2, 4) plt.title("(d) 角谱图") plt.angle_spectrum(audio, Fs=fs, color='C2') plt.xlabel("频率", fontsize=13) plt.ylabel("角度 (弧度)", fontsize=13) plt.tight_layout() plt.show()
语谱图
横坐标是时间,纵坐标是频率,坐标点值为语音数据能量,能量值的大小是通过颜色来表示的,颜色越深表示该点的能量越强。一条条横方向的条纹,称为“声纹”。它因人而异,即不同讲话者语谱图声纹是不同的,因而可以用声纹鉴定不同的讲话人。语谱图中的花纹有横杠、乱纹和竖直条等,横杠是和时间轴平行的几条深色带纹,它们相应于短时谱中的几个凸出点,即共振峰,有没有横杠出现是判断它是否是浊音的重要标志。

import matplotlib.pyplot as plt import matplotlib import librosa from matplotlib.ticker import FuncFormatter plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签 plt.rcParams['axes.unicode_minus'] = False # 用来正常显示符号 fs = 16000 audio, _ = librosa.load("../wav_data/p225_001.wav", sr=fs) fig = plt.figure() norm = matplotlib.colors.Normalize(vmin=-200, vmax=-40) ax_1 = plt.subplot(1, 1, 1) plt.title("语谱图", fontsize=14) plt.specgram(audio, Fs=fs, scale_by_freq=True, sides='default', cmap="jet", norm=norm) plt.xlabel('时间/s', fontsize=14) plt.ylabel('频率/kHz', fontsize=14) plt.xticks(fontsize=14) plt.yticks(fontsize=14) # 左、底、右、高 设置colorbar位置 cbar_ax = fig.add_axes([0.92, 0.115, 0.015, 0.76]) plt.colorbar(norm=norm, cax=cbar_ax) # -200 -50 # https://blog.csdn.net/monotonomo/article/details/83826621 def formatnum(x, pos): return '$%d$' % (x / 1000) formatter = FuncFormatter(formatnum) ax_1.yaxis.set_major_formatter(formatter) # plt.tight_layout() plt.show()
频谱图和语谱图的区别:
- 语谱图表示不同时间内不同频率的能量分布,
- 频谱图表示的是不同频率下语音的能量分布
咱们趁热打铁,再来看看对比一下,了解一下幅度谱、相位谱、实部谱和虚部谱图

import librosa import numpy as np import scipy.signal as signal import matplotlib.pyplot as plt # 读取音频文件 from matplotlib.ticker import FuncFormatter sr = 16000 wav = librosa.load("../wav_data/p225_001.wav", sr=sr)[0] frequencies, times, stft = signal.stft(wav, sr, window='hann', nperseg=512, noverlap=256) print(frequencies.shape, times.shape, stft.shape) # (257,) (130,) (257, 130) # 计算幅度谱和相位谱 amplitude = np.abs(stft) phase = np.angle(stft) # 计算实数谱和虚数谱 real_part = np.real(stft) imaginary_part = np.imag(stft) # 幅度谱 ax_1 = plt.subplot(2, 2, 1) plt.pcolormesh(times, frequencies, 10 * np.log10(amplitude ** 2 + 1e-8), cmap='jet') plt.colorbar(format='%+2.0f dB') plt.ylabel('Frequency [kHz]') plt.xlabel('Time [sec]') plt.title('Amplitude Spectrum') # 相位谱 ax_2 = plt.subplot(2, 2, 2) plt.pcolormesh(times, frequencies, phase, cmap='jet') plt.colorbar(format='%+2.0f dB') plt.ylabel('Frequency [kHz]') plt.xlabel('Time [sec]') plt.title('Phase Spectrum') # 实数谱 ax_3 = plt.subplot(2, 2, 3) plt.pcolormesh(times, frequencies, 10 * np.log10(real_part ** 2 + 1e-8), cmap='jet') plt.colorbar(format='%+2.0f dB') plt.ylabel('Frequency [kHz]') plt.xlabel('Time [sec]') plt.title('Real Part Spectrum') # 虚数谱 ax_4 = plt.subplot(2, 2, 4) plt.pcolormesh(times, frequencies, 10 * np.log10(imaginary_part ** 2 + 1e-8), cmap='jet') plt.colorbar(format='%+2.0f dB') plt.ylabel('Frequency [kHz]') plt.xlabel('Time [sec]') plt.title('Imaginary Part Spectrum') # https://blog.csdn.net/monotonomo/article/details/83826621 def formatnum(x, pos): return '$%d$' % (x / 1000) formatter = FuncFormatter(formatnum) ax_1.yaxis.set_major_formatter(formatter) ax_2.yaxis.set_major_formatter(formatter) ax_3.yaxis.set_major_formatter(formatter) ax_4.yaxis.set_major_formatter(formatter) plt.tight_layout() plt.show()
画频谱图的API函数(随便选一个都可以哈):
plt.specgram(audio, Fs=fs, scale_by_freq=True, sides='default', cmap="jet") plt.pcolormesh(times, frequencies, LogMag, cmap='jet') plt.imshow(LogMag, cmap='jet', aspect='auto', origin='lower') librosa.display.specshow(LogMag, y_axis='linear', x_axis='time', cmap='jet')
增益单位dB
dB衡量一个数大小的单位,具体计算方法如下:
案例:
X = 100000 =
X = 0.000000000000001 =
平均功率:
求语音的音量表示有两种表示方法:
方法一:基于语音最大峰值的dBFS
def max_dbfs(sample_data): # 基于样本最大能量峰值的dBFS rms = max(abs(np.min(sample_data)), abs(np.max(sample_data))) dbfs = 20.0 * math.log10(max(1e-16, rms)) return dbfs
方法二:基于语音平方根(平均功率)的dBFS
def mean_dbfs(sample_data): rms = math.sqrt(np.mean(np.square(sample_data, dtype=np.float64))) dbfs = 20.0 * math.log10(max(1e-16, rms)) + 3.0103 return dbfs
声压级
作为音频从业人员,经常说:“大街上能到50分贝啊”,“你们新出的耳机降噪能达到35分贝啊”,“在30分贝下,声音质量...”,等等,他们说的分贝是怎么计算的呢呢?
分贝(decibel)主要用于度量声音强度,常用dB表示。音频中常用分贝来做计量单位,但是dB在不同领域有不同的物理意义,即便是在音频中,分贝也有着不同的含义。如信噪比也用dB。声压级计算公式为:
其中P是待测声压,
专业人士为了准确的表达声压级的概念,在使用dB时会增加一个后缀:SPL, 也就是sound pressure level的缩写,以便明确其物理意义。
再者,我们来看看主动降噪,也就是ANC 的降噪能力的评价方式,我们常常看到蓝牙耳机的宣传,ANC达到35dB啦之类,这是什么意思呢?实际上,这些宣传往往指的是,在整个耳机的频率范围内,开启和关闭主动降噪时,在人耳内部听到的环境声衰减了多少比例。借用信噪比的公示,这里应该是
最后我们看看开头的讨论,第一个显然指的是声压级,第二个是指开启和关闭主动降噪时声音的比例,第三个是指信噪比。
前端语音算法
前端语音信号处理的意义:面对噪声、干扰、声学回声、混响等不利因素的影响,运用信号处理、机器学习等手段,提高目标语音的信噪比或主观听觉感受,增强语音交互后续环节的稳健性。
让人听清:更高的信噪比,更好的主观听觉感受和可懂度,更低的处理延时。
让机器听清:更好的声学模型适配,更高的语音识别性能。
总结:语音信号处理的目标,是为了让人和机器更容易听清语音,让语音交互更加自然和无约束。
针对不同的干扰因素,采用不同的信号处理算法
去回声——去混响[可选]——盲源分离[可选]——波束赋形——语音降噪——自动增益控制
AEC
消除设备自身产生的回声干扰,最早应用于全双工语音通信、视频会议,在语音交互中起到打断唤醒的作用
主要模块
时延估计(需要把参考信号和输入信号中跟参考信号高度相关的,时间上对齐)
线性回声消除(回声消除的核心,设计一组线性自适应滤波器,消除设备自身产生的回声,同时尽可能的保护近端语音不要受到损伤)
双讲检测(控制在线性回声消除阶段,究竟什么时刻该做什么事情,有以下几种状态:只有远讲回声信号存在、既有远讲回声信号又有近端的语音信号、
残余回声抑制(把前面线性回声消除部分没有消除干净遗漏过来的残余回声,做进一步回声消除)
参考信号:近端扬声器播放的语音信号(没有经过污染的纯净语音),远端输入信号,近端输出信号
输入信号:麦克风接收到的输入信号(参考信号的回声+近端说话语音)
输出信号:近端说话语音信号
解混响
混响是由语音的多径效应所产生,在数学表达上是一个近场的纯净语音信号去卷积一个房间的冲击响应函数(RIR),这样的话能得到一个混响的语音信号。我们希望把混响的干扰因素消除掉,技术上有以下几种方法:
盲反卷积法[NeelyandAllen,1979]:直接去估计房间冲击响应函数的逆函数,如果把RIR当做是一个滤波器的话,我们直接去估计RIR的一个逆滤波器,然后把逆滤波器作用在带有混响的语音上,就得到了纯净语音信号。(“盲”:即没有任何的先验信息(既不知道原始信号的统计信息,也不知道房间的冲击响应函数),这种情况下想要恢复原始语音信号是非常困难的,所以我们只能假设完全没有噪声的场景,并且假设房间的冲击响应函数RIR是不变的,只有在这种比较严格的假设之下,才能得到相对较好的结果,但是这种假设在我们的实际情况当中是不会得到满足的,所以这种技术缺陷也是比较明显的)
加权预测误差[WPE,Takuya,2012]:因为语音信号具有线性预测特性(如果把语音信号当做是一系列采样点信号的话,那么下一个采样点可以用当前时刻以及当前时刻之前的若干采样点的值去预测出下一个时刻采样点的值),WPE认为混响可以分为早期混响和晚期混响,早期混响对于我们人的听觉感受系统没有负向作用,相反可能还有正面作用;晚期混响相对于房间冲击响应的拖尾的声音。那么加权预测误差则是希望估计一个最优的线性预测滤波器,这个滤波器的作用能够将房间冲击响应函数消除晚期混响的影响,多用于多通道。适用于单通道和多通道场景,多通道效果更好。
麦克风阵列波束形成:混响是多路径反射到达麦克风的,所以入射方向是全向的入射,而语音是方向性的入射,所以可以设计一个波束,拾取手机语音入射方向的语音。这样其他方向的混响就会被抑制
深度学习用于解混响[Han,2015]:通过DAE、DNN、LSTM或者GAN,实现频谱映射,端到端映射:带有干扰的语音信号频谱直接映射成为纯净语音信号的频谱,mask(掩膜,适用于乘性噪声):在当前的一个时频点上,是有效语音多还是带噪语音多,如果有效语音多则提取,如果带噪语音多则抑制。
波束形成
波束形成技术多用于多通道语音增强、信号分离、去混响、声源定位,
原理:通过空间信息来区分不同位置说话人语音,麦克风阵列形成波束只接受目标说话人语音,其他方向进行屏蔽。
EQ
EQ是均衡器的缩写。它的基本作用是通过对声音某一个或多个频段进行增益或衰减,达到调整音色的目的。当然,EQ还有一个显著的功能,降噪。EQ通常包括如下参数:F(requency),频率――这是用于设定你要进行调整的频率点用的参数;G(ain),增益――用于调整在你设定好的F值上进行增益或衰减的参数;Q(uantize)――用于设定你要进行增益或衰减的频段 “宽度”。
详解请移步:EQ 均衡器
噪声抑制
消除或抑制环境噪声,增强语音信号
- 基于统计模型的方法(GMM...)
- 最小均方误差MMSE、最大似然估计ML、最大后验估计MAP
基于子空间的方法(MUSIC算法)
- 利用语音和噪声的不相关性,借助特征值/奇异值分解手段分解到子空间处理
- 语音增强的核心在于噪声估计
- 递归平均、最小值追踪、直方图统计是比较常用的噪声估计手段
- 基于深度学习的语音增强方法
- 两大类方法:Masking && Mapping
- 通过DNN、CNN、RNN或者GAN,在频域或时域实现(多为频域)
幅度控制
AGC
自动增益控制(Automatic Gain Control),AGC可以自动调麦克风的收音量,使与会者收到一定的音量水平,不会因发言者与麦克风的距离改变时,声音有忽大忽小声的缺点。
DRC(动态压缩控制)
动态范围控制(Dynamic Range Control),当输出的音频信号不是很大的时候,系统会按照原来的设定输出,但是当输出的音频信号过大的时候,为了保护喇叭DRC会将输出信号的幅度进行压缩将其限制在一个范围内。因为输出的音频信号过大会引起削峰,从而引起音频失真,并且损坏喇叭,所以需要有DRC的作用来将输出限制在一定的范围内。在信号很小的时候DRC是不会起作用的,只有当输出信号的功率超过了你设定的DRC门限的时候DRC才会工作。
等响度
声音实际响度和人耳实际感受的响度并不完全呈线性关系,在小音量的时候,人耳对中高频的听觉会有生理性衰减,音量越小,这种衰减越明显。等响度控制其作用是在低音量时提升高频和低频成分的音量,使得低、中、高部分的响度比例保持和在大音量时的响度比例相同。等响度控制即满足此要求,等响度控制一般为8dB或10dB。 为了在小音量的时候保持人耳听觉相对大音量时高低频段听觉的等响度效果,有些前级放大器插入了等响度效果电路,原理是在小音量的时候适当提升中高频段放大比例,达到人耳听感的一致性。
3D环绕音
A3D是Aureal Semiconductor开发的一种崭新的互动3D定位音效技术,使用这一技术的软件(特别是游戏)可以根据软件中交互式的场景、声源变化而输出相应变化的音效,产生围绕听者的极其逼真的3D定位音效,带来真实的听觉体验,而这一切只需通过一对普通的音箱或耳机就能实现。
A3D技术与传统做法最大的不同之处,在于它可以只利用一组喇叭或者是耳机,就可以发出逼真的立体声效,定位出环绕使用者身边不同位置的音源。这种音源追踪的能力,就叫做定位音效,它使用当时的HRTF的功能来达到这种神奇的效果。
虚拟低音
由于具有体积小、功耗低、成本低廉的优点,小型扬声器一直是笔记本电脑、智能手机、MP3、MP4等便携式媒体设备必不可少的单元。然而,限于体积小的原因,小型扬声器的低频还原能力较差,满足不了人们对高音质的需求。如何改进小型扬声器的低频音质一直是个热门问题。基于虚拟音调现象的虚拟低音处理技术是一种有效改善小型扬声器低频音质的方法。目前,虚拟低音实现算法主要有两种:VB Phase Vocoder算法和MaxxBass算法。两种方法相比之下,VB Phase Vocoder算法比MassBass算法较为灵活、响度计算较为精确;而且VB Phase Vocoder算法的噪声控制较好,不会产生像MaxxBass算法中的非线性失真。
变音
变声模块主要实现变声功能:如娃娃音、怪兽音、男变女、女变男。
数学基础
欧拉公式:
FFT变换:
移位:设某一序列
翻褶:设某一序列
和:
积:
累加:
差分 (一阶):
线性卷积 (linear convolution) :
由卷积的定义可知,卷积在图形表示上可分为四步:翻褶、移位、相乘、相加。
线性卷积的应用:模拟远场数据
近讲纯净语音信号卷积一个房间冲击响应(RIR)=远场语音信号(带有混响)
圆周移位 (circular shift) :
其中,
圆周卷积 (circular convolution):
如果
则
结论:在时域的圆周卷积相当于在频域这两个傅里叶变换的乘积
注意:与线性卷积相比,圆周卷积多了 周期延拓 和 取主值序列 两个步骤。因此必须指定圆周卷积的点数 N 。
圆周卷积和线性卷积的关系
圆周卷积
线性卷积
给定两个有限长序列
一般的,如果两个有限长序列的长度为
和 ,且满足 ,则圆周卷积的后N_1-N_2+1个点,与线性卷积的结果一致。
线性相关(linear correlation) :
圆周相关(circular correlation):
如果:
则
圆周相关和线性相关的关系线:一般的,如果两个有限长序列的长度为
复数知识
复数
实部、虚部(直角坐标系):
幅值、相位(指数系):
极坐标表示法:
指数系
实部:
虚部:
幅值:
相角(以弧度为单位rad):
相角(以角度为单位deg):
相位: phase = np.exp(1j * np.angle(D(F, T)))
离散傅里叶变换的几个问题
对连续信号进行STFT处理,等价于截取一段时间信号,对其进行周期性延拓,从而变成无限长序列,并对该无限长序列做FFT变换,这一截断并不符合FFT的定义。因此会导致 频谱泄漏 和 频谱混叠。
频谱泄漏
现象:当信号的频率分量没有精确对齐到FFT的频率刻度时,能量会从一个频率分量“泄漏”到其他频率分量。这通常发生在对非周期信号或信号的采样长度不足以完整表示周期的信号进行DFT时。
原因:在有限时间窗口内对信号进行分析时,相当于对信号乘以一个矩形窗口。矩形窗口在频域中的响应具有主瓣和旁瓣,而这些旁瓣会引起频谱能量的扩散,也就是泄漏。
如果信号的频率不是FFT的离散频率之一,主频率分量的能量就会扩散到相邻的频率分量中。
影响:导致频谱图上频率分量的幅度和位置失真,使得分析结果不够准确。
解决方案:使用加窗技术(如汉明窗、汉宁窗、Blackman窗等)可以减少频谱泄漏问题,这些窗函数在频域中具有较小的旁瓣能量,可以减少能量的扩散。
频谱混叠
现象:频谱混叠是指当对信号进行采样时,采样频率低于奈奎斯特频率(Nyquist frequency)时,高于奈奎斯特频率的信号频率分量会被折叠到低频段,导致频率分量重叠,从而产生误差。
原因:根据奈奎斯特采样定理,采样频率必须至少是信号最高频率分量的两倍,否则高频信号会折叠到较低频率处,导致信号的频谱失真。
影响:混叠会导致原始信号的高频分量出现在低频部分,使得频谱分析和重建时无法区分原始频率与折叠频率,导致信号失真。
解决方案:在采样之前,使用低通滤波器(抗混叠滤波器)去除信号中的高频分量,确保所有信号频率都在奈奎斯特频率以下。增加采样率,使其至少是信号中最高频率的两倍。
栅栏效应
因为DFT 计算频谱只限制在离散点上的频谱,也就是
减小栅栏效应的方法就是要是频域抽样更密,即增加频域抽样点数,就好像距离“栅栏”的距离边远一些。在不改变时域信号的情况下,必然是在时域信号末端 补零 。补零后的时域数据,在频谱中的谱线更密,原来看不到的谱分量就有可能看到了。
语音信号DFT的共轭对称性
时域中的语音信号,经过离散傅里叶变换DFT后的频谱是共轭对称的。
参考文献
【CSDN】信号的带宽、传输速率、采样率的关系
【博客园】声音之分贝
【CSDN】关于音频EQ、DRC、等响度、3D环绕音、虚拟低音、变音、AEC、AGC、ANS等解释
TODO:
采样量化编码以后做编解码器的时候建议写的更加详细一点
作者:凌逆战
欢迎任何形式的转载,但请务必注明出处。
限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。
本文章不做任何商业用途,仅作为自学所用,文章后面会有参考链接,我可能会复制原作者的话,如果介意,我会修改或者删除。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· AI Agent开发,如何调用三方的API Function,是通过提示词来发起调用的吗