基于卷积神经网络和连接性时序分类的语音识别系统,含核心Python工程源代码(深度学习)个人可二次开发

news2024/11/24 12:45:41

目录

  • 前言
  • 总体设计
    • 系统整体结构图
    • 系统流程图
  • 运行环境
  • 模块实现
    • 1. 特征提取
    • 2. 声学模型
    • 3. CTC 解码
    • 4. 语言模型
  • 系统测试
  • 工程源代码下载
  • 其它资料下载

在这里插入图片描述

前言

本项目基于卷积神经网络和连接性时序分类方法,采用中文语音数据集进行训练,实现声音转录为中文拼音,并将拼音序列转换为中文文本。

本项目提供的是一套完整的语音识别解决方案,可以帮助用户快速搭建语音识别应用,适用于多种场景下的需求。伙伴们可以通过该工程源码,进行个人二次开发,拓展项目的应用场景。

总体设计

本部分包括系统整体结构图和系统流程图。

系统整体结构图

系统整体结构如图所示。

在这里插入图片描述

系统流程图

系统流程如图所示。

在这里插入图片描述

运行环境

需要 Python3.6 及以上配置,在 Windows 环境下推荐下载 Anaconda 完成 Python 所需的配置,推荐选用 Anaconda 自带的 Spyder 系统。

添加镜像为清华大学后,直接利用 conda install tensorflow 即可安装。除此之外需要额外下载 python_speech_features TensorFlow Keras Numpy wave matplotlib math Scipy h5py http urllib 等安装包库,推荐 conda install 下载安装。

conda install python_speech_features, TensorFlow, Keras,Numpy,wave,matplotlib,math, Scipy,h5py,http,urllib`

模块实现

本项目包括 4 个模块,特征提取、声学模型、CTC 解码、语言模型,下面分别给出各模块的功能介绍及相关代码。

1. 特征提取

将普通的.wav语音信号通过分帧加窗等操作转换为神经网络需要的二维频谱图像信号,即语谱图。

代码如下:

import wave
import numpy as np
from python_speech_features import mfcc, delta
from scipy.fftpack import fft

def read_wav_data(filename): 
    ''' 
    读取.wav 文件,返回声音信号的时域谱矩阵和播放时间 
    ''' 
    wav = wave.open(filename,"rb") # 打开.wav 格式的声音文件流 
    num_frame = wav.getnframes() # 获取帧数 
    num_channel=wav.getnchannels() # 获取声道数 
    framerate=wav.getframerate() # 获取帧速率 
    num_sample_width=wav.getsampwidth() # 获取实例的比特宽度,即每一帧的字节数 
    str_data = wav.readframes(num_frame) # 
    wav.close() # 关闭流 
    wave_data = np.fromstring(str_data, dtype = np.short) # 将声音文件数据转换为数组矩阵形式 
    wave_data.shape = -1, num_channel # 按照声道数将数组整形,单声道时是一列数组,双声道时是两列矩阵 
    wave_data = wave_data.T # 将矩阵转置 
    #wave_data = wave_data 
    return wave_data, framerate

def GetMfccFeature(wavsignal, fs): 
    #获取输入特征 
    feat_mfcc=mfcc(wavsignal[0],fs) 
    feat_mfcc_d=delta(feat_mfcc,2) 
    feat_mfcc_dd=delta(feat_mfcc_d,2) 
    #返回值分别是 mfcc 特征向量的矩阵及其一阶差分和二阶差分矩阵 
    wav_feature = np.column_stack((feat_mfcc, feat_mfcc_d, feat_mfcc_dd)) 
    return wav_feature 

def GetFrequencyFeature(wavsignal, fs): 
    if(16000 != fs): 
        raise ValueError('[Error] ASRT currently only supports wav audio files with a sampling rate of 16000 Hz, but this audio is ' + str(fs) + ' Hz. ') 
    # wav 波形 加时间窗以及时移 10ms 
    time_window = 25 # 单位 ms 
    data_input = [] 
    #print(int(len(wavsignal[0])/fs*1000 - time_window) // 10) 
    wav_length = len(wavsignal[0]) #计算一条语音信号的原始长度 
    range0_end = int(len(wavsignal[0])/fs*1000 - time_window) // 10 #计算循环终止的位置,也就是最终生成的窗数 
    for i in range(0, range0_end): 
        p_start = i * 160 
        p_end = p_start + 400 
        data_line = [] 
        for j in range(p_start, p_end): 
            data_line.append(wavsignal[0][j])
            #print('wavsignal[0][j]:\n',wavsignal[0][j]) 
        #data_line = abs(fft(data_line)) / len(wavsignal[0]) 
        data_line = fft(data_line) / wav_length 
        data_line2 = [] 
        for fre_sig in data_line: 
            #分别取出频率信号的实部和虚部作为语音信号的频率特征 
            #直接使用复数会被 numpy 将虚部丢弃,造成信息丢失 
            #print('fre_sig:\n',fre_sig) 
            data_line2.append(fre_sig.real) 
            data_line2.append(fre_sig.imag) 
        data_input.append(data_line2[0:len(data_line2)//2]) #除以 2 是取一半数据,因为是对称的 
        #print('data_input:\n',data_input) 
        #print('data_line:\n',data_line) 
    #print(len(data_input),len(data_input[0])) 
    return data_input 

def GetFrequencyFeature2(wavsignal, fs): 
    if(16000 != fs): 
        raise ValueError('[Error] ASRT currently only supports wav audio files with a sampling rate of 16000 Hz, but this audio is ' + str(fs) + ' Hz. ') 
    # wav 波形 加时间窗以及时移 10ms 
    time_window = 25 # 单位 ms 
    window_length = fs / 1000 * time_window #计算窗长度的公式,目前全部为 400 固定值 
    wav_arr = np.array(wavsignal) 
    #wav_length = len(wavsignal[0]) 
    wav_length = wav_arr.shape[1] 
    range0_end = int(len(wavsignal[0])/fs*1000 - time_window) // 10 #计算循环终止的位置,也就是最终生成的窗数 
    data_input = np.zeros((range0_end, 200), dtype = np.float) #用于存放最终的频率特征数据 
    data_line = np.zeros((1, 400), dtype = np.float) 
    for i in range(0, range0_end): 
        p_start = i * 160 
        p_end = p_start + 400 
        data_line = wav_arr[0, p_start:p_end] 
        ''' 
        x=np.linspace(0, 400 - 1, 400, dtype = np.int64) 
        w = 0.54 - 0.46 * np.cos(2 * np.pi * (x) / (400 - 1) ) # 汉明窗 
        data_line = data_line * w # 加窗 
        ''' 
        data_line = np.abs(fft(data_line)) / wav_length 
        data_input[i]=data_line[0:200] #设置为 400 除以 2 的值(即 200)是取一半数据,因为是对称的 
        #print(data_input.shape) 
    return data_input
 
x=np.linspace(0, 400 - 1, 400, dtype = np.int64) 
w = 0.54 - 0.46 * np.cos(2 * np.pi * (x) / (400 - 1) ) # 汉明窗

def GetFrequencyFeature3(wavsignal, fs): 
    if(16000 != fs): 
        raise ValueError('[Error] ASRT currently only supports wav audio files with a sampling rate of 16000 Hz, but this audio is ' + str(fs) + ' Hz. ') 
    # wav 波形 加时间窗以及时移 10ms 
    time_window = 25 # 单位 ms 
    window_length = fs / 1000 * time_window #计算窗长度的公式,目前全部为 400 固定值 
    wav_arr = np.array(wavsignal) 
    #wav_length = len(wavsignal[0]) 
    wav_length = wav_arr.shape[1] 
    range0_end = int(len(wavsignal[0])/fs*1000 - time_window) // 10 #计算循环终止的位置,也就是最终生成的窗数 
    data_input = np.zeros((range0_end, 200), dtype = np.float) #用于存放最终的频率特征数据 
    data_line = np.zeros((1, 400), dtype = np.float) 
    for i in range(0, range0_end): 
        p_start = i * 160 
        p_end = p_start + 400 
        data_line = wav_arr[0, p_start:p_end] 
        data_line = data_line * w # 加窗 
        data_line = np.abs(fft(data_line)) / wav_length 
        data_input[i]=data_line[0:200] # 设置为 400 除以 2 的值(即 200)是取一半数据,因为是对称的 
        #print(data_input.shape) 
    data_input = np.log(data_input + 1) 
    return data_input

def GetFrequencyFeature4(wavsignal, fs): 
    ''' 
    主要是用来修正 3 版的 bug 
    ''' 
    if(16000 != fs): 
        raise ValueError('[Error] ASRT currently only supports wav audio files with a sampling rate of 16000 Hz, but this audio is ' + str(fs) + ' Hz. ') 
    # wav 波形 加时间窗以及时移 10ms 
    time_window = 25 # 单位 ms 
    window_length = fs / 1000 * time_window #计算窗长度的公式,目前全部为 400 固定值 
    wav_arr = np.array(wavsignal) 
    #wav_length = len(wavsignal[0]) 
    wav_length = wav_arr.shape[1] 
    range0_end = int(len(wavsignal[0])/fs*1000 - time_window) // 10 + 1 #计算循环终止的位置,也就是最终生成的窗数 
    data_input = np.zeros((range0_end, window_length // 2), dtype = np.float) #用于存放最终的频率特征数据
    data_line = np.zeros((1, window_length), dtype = np.float) 
    for i in range(0, range0_end): 
        p_start = i * 160 
        p_end = p_start + 400 
        data_line = wav_arr[0, p_start:p_end] 
        data_line = data_line * w # 加窗 
        data_line = np.abs(fft(data_line)) / wav_length 
        data_input[i]=data_line[0: window_length // 2] # 设置为 400 除以 2 的值(即 200)是取一半数据,因为是对称的 
        #print(data_input.shape) 
    data_input = np.log(data_input + 1) 
    return data_input
import math
import numpy as np
import matplotlib.pyplot as plt
import time

def wav_scale(energy):
    '''
    语音信号能量归一化
    '''
    means = energy.mean()  # 均值
    var = energy.var()  # 方差
    e = (energy - means) / math.sqrt(var)  # 归一化能量
    return e

def wav_scale2(energy):
    '''
    语音信号能量归一化
    '''
    maxnum = max(energy)
    e = energy / maxnum
    return e

def wav_scale3(energy):
    '''
    语音信号能量归一化
    '''
    for i in range(len(energy)):
        # if i == 1:
        #     # print('wavsignal[0]:\n {:.4f}'.format(energy[1]),energy[1] is int)
        energy[i] = float(energy[i]) / 100.0
        # if i == 1:
        #     # print('wavsignal[0]:\n {:.4f}'.format(energy[1]),energy[1] is int)
    return energy

def wav_show(wave_data, fs):
    # 显示出来声音波形
    time = np.arange(0, len(wave_data)) * (1.0 / fs)  # 计算声音的播放时间,单位为 s
    # 画声音波形
    # plt.subplot(211)
    plt.plot(time, wave_data)
    # plt.subplot(212)
    # plt.plot(time, wave_data[1], c="g")
    plt.show()

def get_wav_list(filename):
    # 读取一个.wav 文件列表,返回一个存储该列表的字典类型值
    txt_obj = open(filename, 'r')  # 打开文件并读入
    txt_text = txt_obj.read()
    txt_lines = txt_text.split('\n')  # 文本分割
    dic_filelist = {}  # 初始化字典
    list_wavmark = []  # 初始化 wav 列表
    for i in txt_lines:
        if (i != ''):
            txt_l = i.split(' ')
            dic_filelist[txt_l[0]] = txt_l[1]
            list_wavmark.append(txt_l[0])
    txt_obj.close()
    return dic_filelist, list_wavmark

def get_wav_symbol(filename):
    '''
    读取指定数据集中,所有 wav 文件对应的语音符号
    返回一个存储符号集的字典类型值
    '''
    txt_obj = open(filename, 'r')  # 打开文件并读入
    txt_text = txt_obj.read()
    txt_lines = txt_text.split('\n')  # 文本分割
    dic_symbol_list = {}  # 初始化字典
    list_symbolmark = []  # 初始化 symbol 列表
    for i in txt_lines:
        if (i != ''):
            txt_l = i.split(' ')
            dic_symbol_list[txt_l[0]] = txt_l[1:]
            list_symbolmark.append(txt_l[0])
    txt_obj.close()
    return dic_symbol_list, list_symbolmark

if __name__ == '__main__':
    wave_data, fs = read_wav_data("A2_0.wav") 
    wav_show(wave_data[0], fs)
    t0 = time.time()
    freimg = GetFrequencyFeature3(wave_data, fs)
    t1 = time.time()
    print('time cost:', t1 - t0)
    freimg = freimg.T
    plt.subplot(111)
    plt.imshow(freimg)
    plt.colorbar(cax=None, ax=None, shrink=0.5)
    plt.show()

2. 声学模型

使用类似 VGG 的深层卷积神经网络作为网络模型,并训练。输入层:200 维的特征值序列,一条语音数据的最大长度设为 1600(大约 16s),隐藏层:卷积池化层,卷积核大小为 3*3,池化窗口大小为 2。隐藏层:全连接层,输出层:全连接层,神经元数量为 self.MS_OUTPUT_SIZE,使用 softmax 作为激活函数,CTC 层:使用 CTC 的 loss 作为损失函数,实现连接性时序多输出模型构建。

from keras.models import Model
from keras.layers import Input, Conv2D, MaxPooling2D, Dropout, Reshape, Dense, Activation, Lambda
from keras.optimizers import Adam
import keras.backend as K

class YourModel:
    def __init__(self):
        self.label_max_string_length = 32 #example value
        self.MS_OUTPUT_SIZE = 256 #example value

    def ctc_lambda_func(self, args):
        # example ctc_lambda_func implementation
        return K.ctc_batch_cost(args[0], args[1], args[2], args[3])

    def build_model(self, input_shape):
        input_data = Input(name='the_input', shape=input_shape, dtype='float32')
        layer_h1 = Conv2D(32, (3, 3), use_bias=False, activation='relu', padding='same', 
                          kernel_initializer='he_normal')(input_data)
        layer_h1 = Dropout(0.05)(layer_h1)
        layer_h2 = Conv2D(32, (3, 3), use_bias=True, activation='relu', padding='same', 
                          kernel_initializer='he_normal')(layer_h1)
        layer_h3 = MaxPooling2D(pool_size=2, strides=None, padding="valid")(layer_h2)
        layer_h3 = Dropout(0.05)(layer_h3)
        layer_h4 = Conv2D(64, (3, 3), use_bias=True, activation='relu', padding='same', 
                          kernel_initializer='he_normal')(layer_h3)
        layer_h4 = Dropout(0.1)(layer_h4)
        layer_h5 = Conv2D(64, (3, 3), use_bias=True, activation='relu', padding='same', 
                          kernel_initializer='he_normal')(layer_h4)
        layer_h6 = MaxPooling2D(pool_size=2, strides=None, padding="valid")(layer_h5)
        layer_h6 = Dropout(0.1)(layer_h6)
        layer_h7 = Conv2D(128, (3, 3), use_bias=True, activation='relu', padding='same', 
                          kernel_initializer='he_normal')(layer_h6)
        layer_h7 = Dropout(0.15)(layer_h7)
        layer_h8 = Conv2D(128, (3, 3), use_bias=True, activation='relu', padding='same', 
                          kernel_initializer='he_normal')(layer_h7)
        layer_h9 = MaxPooling2D(pool_size=2, strides=None, padding="valid")(layer_h8)
        layer_h9 = Dropout(0.15)(layer_h9)
        layer_h10 = Conv2D(128, (3,3), use_bias=True, activation='relu', padding='same', 
                           kernel_initializer='he_normal')(layer_h9)
        layer_h10 = Dropout(0.2)(layer_h10)
        layer_h11 = Conv2D(128, (3,3), use_bias=True, activation='relu', padding='same', 
                           kernel_initializer='he_normal')(layer_h10)
        layer_h12 = MaxPooling2D(pool_size=1, strides=None, padding="valid")(layer_h11)
        layer_h12 = Dropout(0.2)(layer_h12)
        layer_h13 = Conv2D(128, (3,3), use_bias=True, activation='relu', padding='same', 
                           kernel_initializer='he_normal')(layer_h12)
        layer_h13 = Dropout(0.2)(layer_h13)
        layer_h14 = Conv2D(128, (3,3), use_bias=True, activation='relu', padding='same', 
                           kernel_initializer='he_normal')(layer_h13)
        layer_h15 = MaxPooling2D(pool_size=1, strides=None, padding="valid")(layer_h14)
        layer_h16 = Reshape((200, 3200))(layer_h15)
        layer_h16 = Dropout(0.3)(layer_h16)
        layer_h17 = Dense(128, activation="relu", use_bias=True, 
                           kernel_initializer='he_normal')(layer_h16)
        layer_h17 = Dropout(0.3)(layer_h17)
        layer_h18 = Dense(self.MS_OUTPUT_SIZE, use_bias=True, 
                          kernel_initializer='he_normal')(layer_h17)
        y_pred = Activation('softmax', name='Activation0')(layer_h18)

        labels = Input(name='the_labels', shape=[self.label_max_string_length], dtype='float32')
        input_length = Input(name='input_length', shape=[1], dtype='int64')
        label_length = Input(name='label_length', shape=[1], dtype='int64')

        ctc_loss = Lambda(self.ctc_lambda_func,output_shape=(self.MS_OUTPUT_SIZE), 
                          name='ctc')([y_pred, labels, input_length, label_length])
        model = Model(inputs=[input_data, labels, input_length, label_length], outputs=ctc_loss)
        model_data = Model(inputs=input_data, outputs=y_pred)
        
        opt = Adam(lr=0.001, beta_1=0.9, beta_2=0.999, decay=0.0, epsilon=10e-8)
        model.compile(loss={'ctc': lambda y_true, y_pred: y_pred}, optimizer=opt)
        test_func = K.function([input_data], [y_pred])
        
        print('[*Info] Create Model Successful, Compiles Model Successful. ')
        return model, model_data

from data_speech import DataSpeech

datapath = '数据保存的路径'
epoch = 10
save_step = 1000
filename = '默认保存文件名'

data = DataSpeech(datapath, 'train')
num_data = data.GetDataNum() #获取数据的数量
yielddatas = data.data_genetator(batch_size, self.AUDIO_LENGTH)

for epoch in range(epoch): #迭代轮数
    print('[running] train epoch %d .' % epoch)
    n_step = 0 #迭代数据数
    while True:
        try:
            print('[message] epoch %d. Have trained datas %d+'%(epoch, n_step*save_step))
            # data_genetator 是一个生成器函数
            #self._model.fit_generator(yielddatas, save_step, nb_worker=2)
            self._model.fit_generator(yielddatas, save_step)
            n_step += 1
        except StopIteration:
            print('[error] generator error. please check data format.')
            break
    
    self.SaveModel(comment='_e_'+str(epoch)+'_step_'+str(n_step * save_step))
    self.TestModel(self.datapath, str_dataset='train', data_count=4)
    self.TestModel(self.datapath, str_dataset='dev', data_count=4)

import random
import time

from data_speech import DataSpeech

# 以下代码为测试模型的函数
def TestModel(self, datapath, str_dataset='dev', data_count=0, out_report=False,
              io_step_print=1, io_step_file=1000, show_ratio=True):
    data = DataSpeech(datapath, str_dataset)
    num_data = data.GetDataNum() # 获取数据的数量
    
    if(data_count <= 0 or data_count > num_data): 
        # 当 data_count 为小于等于 0 或者大于测试数据量的值时,则使用全部数据来测试
        data_count = num_data
        
    try:
        ran_num = random.randint(0,num_data - 1) # 获取一个随机数
        words_num = 0
        word_error_num = 0
        nowtime = time.strftime('%Y%m%d_%H%M%S',time.localtime(time.time()))
        
        if(out_report == True):
            txt_obj = open('Test_Report_' + str_dataset + '_' + nowtime + '.txt', 'w', encoding='UTF-8') 
        
        txt = '测试报告\n 模型编号 ' + ModelName + '\n\n'
        
        for i in range(data_count):
            data_input, data_labels = data.GetData((ran_num + i) % num_data) 
            
            # 数据格式出错处理 开始
            # 当输入.wav 文件过长时自动跳过该文件,转而使用下一个.wav 文件来运行
            num_bias = 0
            while(data_input.shape[0] > self.AUDIO_LENGTH):
                print('*[Error]','wave data lenghth of num',(ran_num + i) % num_data, 'is too long.','\n A Exception raise when test Speech Model.')
                num_bias += 1
                data_input, data_labels = data.GetData((ran_num + i + num_bias) % num_data) 
            
            # 数据格式出错处理,结束
            
            pre = self.Predict(data_input, data_input.shape[0] // 8)
            words_n = data_labels.shape[0] 
            words_num += words_n 
            edit_distance = GetEditDistance(data_labels, pre) 
            
            if(edit_distance <= words_n): 
                word_error_num += edit_distance 
            else: 
                word_error_num += words_n 
            
            if((i % io_step_print == 0 or i == data_count - 1) and show_ratio == True):
                print('Test Count: ',i,'/',data_count)
            
            if(out_report == True):
                if(i % io_step_file == 0 or i == data_count - 1):
                    txt_obj.write(txt)
                    txt = ''
                
                txt += str(i) + '\n'
                txt += 'True:\t' + str(data_labels) + '\n'
                txt += 'Pred:\t' + str(pre) + '\n'
                txt += '\n'
        
        print('*[Test Result] Speech Recognition ' + str_dataset + ' set word error ratio: ', 
              word_error_num / words_num * 100, '%')
        
        if(out_report == True):
            txt += '*[Test Result] Speech Recognition ' + str_dataset + ' set word error ratio:' + 
                   str(word_error_num / words_num * 100) + ' %'
            txt_obj.write(txt)
            txt_obj.close()
    
    except StopIteration:
        print('[Error] Model Test Error. please check data format.')
    
# 加载模型
self._model.load_weights(filename)
self.base_model.load_weights(filename + '.base')

# 保存模型
self._model.save_weights(filename + comment + '.model')
self.base_model.save_weights(filename + comment + '.model.base')
self._model.save(filename + comment + '.h5')
self.base_model.save(filename + comment + '.base.h5')

f = open('step'+ModelName+'.txt','w')
f.write(filename+comment)
f.close()

3. CTC 解码

通过 CNN 提取文本图片的 Feature map,将每一个 channel 作为 [公式] 的时间序列输入到 LSTM 中。

用 w 表示 LSTM 的参数,则 LSTM 可以表示为一个函数:y=Nw(x)。

定义输入 x 的时间步为 T,每个时间步上的特征维度记作 m,表示 m 维特征。
(x1,x2,…,xT)xt=(xt1,xt2,…,xtm)

输出时间步也为 T,和输入一一对应,每个时间步的输出维度作为 n,表示 n 维输出,实际
上是 n 个概率。
(y1,y2,…,yT)yt=(yt1,yt2,…,ytn)

假设对 26 个英文字符进行识别,考虑到有些位置没有字符,定义一个-作为空白符加入到字符集合 L′={a,b,c,…,x,y,z}∪{−}=L∪{−}={a,b,c,…,x,y,z,−},那么对于 LSTM 而言每个时间步的输出维度 n 就是 27,表示 27 个字符在这个时间步上输出的概率。

4. 语言模型

from typing import List

class PinyinDecoder:
    def __init__(self, pinyin_dict: dict):
        self.dict_pinyin = pinyin_dict

        # 加载模型
        with open('model1.bin', 'rb') as f:
            self.model1 = pickle.load(f)
        with open('model2.bin', 'rb') as f:
            self.model2 = pickle.load(f)

    def decode(self, py_list: List[str], max_error: float) -> List[List[str]]:
		'''
		实现拼音向文本的转换
		基于马尔可夫链
		'''
        list_words = []
        num_pinyin = len(py_list)
        for i in range(num_pinyin):
            ls = ''
            if(py_list[i] in self.dict_pinyin): 
                ls = self.dict_pinyin[py_list[i]]
            else:
                break
            if(i == 0):
                num_ls = len(ls)
                for j in range(num_ls):
                    tuple_word = ['', 0.0]
                    tuple_word = [ls[j], 1.0]
                    list_words.append(tuple_word)
                continue
            else:
                list_words_2 = []
                num_ls_word = len(list_words)
                for j in range(0, num_ls_word):
                    num_ls = len(ls)
                    for k in range(0, num_ls):
                        tuple_word = ['', 0.0]
                        tuple_word = list(list_words[j])
                        tuple_word[0] = tuple_word[0] + ls[k]
                        tmp_words = tuple_word[0][-2:]
                        if(tmp_words in self.model2):
                            tuple_word[1] = tuple_word[1] * float(self.model2[tmp_words]) / float(self.model1[tmp_words[-2]])
                        else:
                            tuple_word[1] = 0.0
                            continue
                        if(tuple_word[1] >= pow(max_error, i)):
                            list_words_2.append(tuple_word)
                list_words = list_words_2
                for i in range(0, len(list_words)):
                    for j in range(i + 1, len(list_words)):
                        if(list_words[i][1] < list_words[j][1]):
                            tmp = list_words[i]
                            list_words[i] = list_words[j]
                            list_words[j] = tmp
                return list_words

    def get_chinese_sentence(self, sentence: str) -> str:
        list_syllable = [] # 存储分离后的拼音
        length = len(sentence)
        for i in range(0, length - 1):
            # 依次从第一个字开始每次连续取两个字拼音
            str_split = list_syllable[i] + ' ' + list_syllable[i+1]
            # 如果这个拼音在汉语拼音状态转移字典里
            if(str_split in self.pinyin):
                # 将第二个字的拼音加入
                list_syllable.append(list_syllable[i+1])
            else:
                # 否则不加入,然后直接将现有的拼音序列进行解码
                str_decode = self.decode(list_syllable, 0.0000)
                #print('decode ',str_tmp,str_decode)
                if(str_decode != []):
                    r += str_decode[0][0]
                # 再从 i+1 开始作为第一个拼音
                list_syllable = [list_syllable[i+1]]
                #print('最后:', str_tmp)
                str_decode = self.decode(list_syllable, 0.0000)
                #print('剩余解码:',str_decode)
                if(str_decode != []):
                    r += str_decode[0][0]
        return r

# 读取拼音汉字的字典文件,返回读取后的字典
def load_pinyin_dict(dictfilename):
    with open(dictfilename, 'r', encoding='UTF-8') as f:
        txt_text = f.read()
    txt_lines = txt_text.split('\n')
    dic_symbol = {}
    for i in txt_lines:
        list_symbol=[]
        if(i!=''):
            txt_l=i.split('\t')
            pinyin = txt_l[0]
            for word in txt_l[1]:
                list_symbol.append(word)
            dic_symbol[pinyin] = list_symbol
    return dic_symbol

# 读取语言模型的文件,返回读取后的模型
def load_language_model(modelLanFilename):
    with open(modelLanFilename, 'r', encoding='UTF-8') as f:
        txt_text = f.read()
    txt_lines = txt_text.split('\n')
    dic_model = {}
    for i in txt_lines:
        if(i!=''):
            txt_l=i.split('\t')
            if(len(txt_l) == 1):
                continue
            dic_model[txt_l[0]] = txt_l[1]
    return dic_model

系统测试

模型训练准确率及测试效果如图所示。

在这里插入图片描述

在这里插入图片描述

工程源代码下载

详见本人博客资源下载页

其它资料下载

如果大家想继续了解人工智能相关学习路线和知识体系,欢迎大家翻阅我的另外一篇博客《重磅 | 完备的人工智能AI 学习——基础知识学习路线,所有资料免关注免套路直接网盘下载》
这篇博客参考了Github知名开源平台,AI技术平台以及相关领域专家:Datawhale,ApacheCN,AI有道和黄海广博士等约有近100G相关资料,希望能帮助到所有小伙伴们。

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

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

相关文章

Java枚举中定义属性

文章目录 1、复习枚举2、自定义属性3、自定义属性枚举类和常量的对比4、常用方法5、枚举自定义属性在开发中的应用&#xff1a;字典表6、补充&#xff1a;入参校验 刚接触枚举时的例子太简单&#xff0c;就一个Season枚举类&#xff0c;里面四个常量值&#xff0c;后来开发中看…

接口幂等方案

文章目录 概要方案乐观锁数据库唯一索引令牌tokentoken通过另一个接口从服务端获取客户端自身生成token 总结 概要 所谓接口幂等性&#xff0c;就是一次和多次请求某一个资源对于资源本身应该具有同样的影响。接口幂等的应用很广&#xff0c;小到防止表单重复提交&#xff0c;…

使用kettle完成学生成绩登记需求

&#xff08;一&#xff09; 使用kettle完成学生成绩登记需求 学生成绩表下表所示。(自己创建一个学生表) 在MySQL中创建一个名为school的数据库&#xff0c;并在school数据库中创建一个名为score的表&#xff0c;使用Kettle将Excel形式的学生成绩表导入MySQL的score表 1&am…

机器视觉海康工业相机SDK参数设置获取

视觉人机器视觉培训-缺陷检测项目-食品行业草鸡蛋外观检测 相机参数类型可分为六类,除 command 参数外,每一类都有其对应的设置与获取函数接口。 表 1 参数类型及对应函数接口介绍 *详细函数接口可参考 SDK 手册: ​C:\Program Files (x86)\MVS\Development\Documentation…

【已解决】微信小程序报错:request 合法域名校验出错 如若已在管理后台更新域名配置,请刷新项目配置后重新编译项目,操作路径:“详情-域名信息”

【已解决】微信小程序报错&#xff1a;request 合法域名校验出错 如若已在管理后台更新域名配置&#xff0c;请刷新项目配置后重新编译项目&#xff0c;操作路径&#xff1a;“详情-域名信息” 场景复现解决方法 知识专栏专栏链接微信小程序专栏https://blog.csdn.net/xsl_hr/c…

工控设备如何防勒索病毒

目前现状 无论是中小企业还是大型企事业单位&#xff0c;均有属于自己的内网或公有云服务器。这些服务器有的是专门的SVN、GIT代码服务器&#xff0c;有的是文档存储服务器&#xff0c;有的是应用服务器。服务器是企业的核心命脉&#xff0c;所有知识产权及多年心血都集中汇总…

LeetCode——Pow(x, n)

一、题目 50. Pow(x, n) - 力扣&#xff08;Leetcode&#xff09; 实现 pow(x, n) &#xff0c;即计算 x 的整数 n 次幂函数&#xff08;即&#xff0c;xⁿ &#xff09;。 示例 1&#xff1a; 输入&#xff1a;x 2.00000, n 10 输出&#xff1a;1024.00000示例 2&#x…

Jetpack Compose中的状态栏适配(Window Insets)

除了app的内容区域外&#xff0c;还有一些其他的固定元素会显示在手机屏幕上&#xff0c;顶部的状态栏、 刘海、 底部的导航栏&#xff0c;还有输入法键盘&#xff0c;它们都是系统的UI&#xff0c; 也叫Insets. 如图所示: 顶部的状态栏通常被用来展示通知, 设备状态等; 底部导…

软考A计划-网络规划设计师-学习笔记-上

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分享&am…

Linux 如何判断文件的类型

在Linux中&#xff0c;我们如何判断一个文件的类型和用户权限呢&#xff1f; 在c语言中&#xff0c;Linux为我们提供了一个结构体stat我们可以通过 #include<sys/stat.h>引入后使用。然后通过stat中的st_mode来判断文件的类型。如下图&#xff0c;我们要知道文件是什么类…

超级入门:R 语言的 5 种基本数据类型

一、R语言简介 R语言是一种用于统计计算和绘图的编程语言&#xff0c;它是由新西兰奥克兰大学的 Ross Ihaka 和 Robert Gentleman 开发的。R语言支持向量和矩阵计算&#xff0c;因此也可以用于数值分析和线性代数。它主要应用于数据分析、统计学习、数据挖掘、数据可视化等领域…

【Springboot】集成QQ邮箱信息发送

系列文章目录 文章目录 系列文章目录前言添加Maven依赖QQ邮箱开启POP服务配置application.properties文件Controller层编写 vue前端&#xff08;也可以直接省略&#xff09; 前言 这篇博客用于简单实现SpringBoot中发送请求&#xff0c;用户可以收到邮件。 添加Maven依赖 <…

ISIS 实验

(1)拓扑图 2&#xff09;需求&#xff1a; -实现PC1和PC2的通信 3&#xff09;配置步骤&#xff1a; -配置接口IP地址 -开启ISIS---类似于在OSPF中创建进程 -配置NET地址---类似于在OSPF中创建区域&#xff0c;指定Router-id -在接口上启用ISIS--类似于在OSPFv2中用ne…

SKNet讲解

SKNet讲解 0. 引言1. 网络结构1.1 Split部分1.2 Fuse部分1.3 Select部分1.4 三分支的情况 2. SKNet网络体系结构3. 分析与解释4. 代码总结 0. 引言 视皮层神经元的感受野大小受刺激的调节&#xff0c;即对不同刺激&#xff0c;卷积核的大小应该不同&#xff0c;但在构建CNN时一…

<DB2>《DB2创建分区表及相关操作》(精华)

《DB2创建分区表及相关操作》 1 基本概念2 操作2.1 查看数据库中存在的分区表2.2 查看分区表详细2.3 断开对数据表的访问连接2.4 备份数据2.5 拆离分区2.6 添加分区2.7 导入数据2.8 校验前后数据2.9 删除临时表数据 1 基本概念 当表中的数据量不断增大&#xff0c;查询数据的速…

使用Kettle实现数据排序

一、Kettle的安装 1.下载Kettle的安装包文件 在Windows系统中打开浏览器&#xff0c;访问Kettle官网&#xff08;https://sourceforge.net/projects/pentaho/&#xff09;&#xff0c;下载Kettle安装文件pdi-ce-9.1.0.0-324.zip。 或者在我的百度网盘分享里面下载 链接&…

【软件测试】软件测试总结笔记(2)

软件测试过程&#xff08;内容&#xff09; 1.单元测试基本概念定义⭐单元测试环境⭐单元测试内容单元测试用例的设计思路⭐单元测试的过程 2. 集成测试集成测试内容集成测试优点⭐集成测试层次集成测试方法Drivers and Stubs ⭐集成策略&#xff08;基于分解的集成&#xff09…

crontab定时任务介绍

1 crontab概述 crontab是linux操作系统上用来设置定时任务的基础命令&#xff0c;是基于crond服务实现任务调度执行。 当安装完成操作系统后&#xff0c;默认会安装crond服务及其附属命令&#xff0c;并且会自动启动crond进程&#xff0c;crond进程每分钟会定期检查是否有要执…

Python量化交易:策略创建运行流程

学习目标 目标 知道策略的创建和运行知道策略的相关设置知道RQ的策略运行流程应用 无 1、体验创建策略、运行策略流程 1.1 创建策略 1.2 策略界面 2、 策略界面功能、运行介绍 2.1 一个完整的策略需要做的事情 选择策略的运行信息&#xff1a; 选择运行区间和初始资金选择回…

水库大坝安全问题有哪些?

我国现有水库大坝9.8万余座&#xff0c;80%水库大坝修建于上世纪50至70年代&#xff0c;受经济、技术等历史因素的影响&#xff0c;存在坝体结构破损、坝基渗漏、坝体渗漏、坝面变形等严重的安全隐患。 一、水库大坝的安全问题主要包括以下几个方面&#xff1a; 1.坝体结构破损…