项目1 AI作曲 本项目基于TensorFlow开发环境,使用LSTM模型,搜集MIDI文件,进行特征筛选和提取,训练生成合适的机器学习模型,实现人工智能作曲。 1.1总体设计 本部分包括整体框架和系统流程。 1.1.1整体框架 整体框架如图11所示。 图11整体框架 1.1.2系统流程 系统流程如图12所示。 图12系统流程 1.2运行环境 本部分包括Python环境、虚拟机环境、TensorFlow环境及Python类库。 1.2.1Python环境 在Windows环境下下载Anaconda,完成Python 2.7及以上版本的环境配置,如图13所示。 图13下载Anaconda界面 1.2.2虚拟机环境 安装VirtualBox,如图14所示。 图14安装VirtualBox界面 如图15所示,安装Ubuntu系统,如果版本号是16.04,那么它属于长期支持版。下载Ubuntu GNOME,如图16所示。打开VirtualBox,如图17所示。 图15安装Ubuntu系统 图16下载Ubuntu GNOME界面 图17打开VirtualBox界面 单击“新建”按钮,出现如图18所示的对话框。 图18虚拟机创建界面 名称可自行定义; 类型选择Linux; 版本选择Ubuntu(64bit); 内存大小可自行设置,建议设置为2048MB以上; 虚拟硬盘选择默认选项,即现在创建虚拟硬盘,之后单击创建按钮,在文件位置和大小对话框中将虚拟硬盘更改为20GB,虚拟机映像文件创建完成。对该映像文件右击进行设置,单击存储按钮进行保存。 选择没有盘片→分配光驱→虚拟光盘文件,添加下载好的Ubuntu GnomeISO镜像文件,然后单击OK按钮。选择install Ubuntu GNOME→Continue→Install Now→Continue→Continue,在Keyboard layout对话框中选择Chinese,单击Continue按钮,等待安装完成后单击Restart Now按钮即可。 (1) 进行Ubuntu的基本配置。 (2) 打开Terminal,安装谷歌输入法,输入如下命令: sudo apt install fcitx fcitx-googlepinyin im-config (3) 安装VIM,输入如下命令: sudo apt install vim (4) 创建与主机共享的文件夹,输入如下命令: mkdir share_folder sudo apt install virtualbox-guest-utils (5) 创建主机文件夹AIMM_Shared,建立主机与虚拟机的共享路径,输入如下命令: sudo mount -t vboxsf AIMM_Shared home/share_folder 1.2.3TensorFlow环境 安装TensorFlow,如图19所示,打开Terminal,输入如下命令: sudo apt-get install python-pip python-dev python-virtualenv virtualenv --system-site-packages tensorflow source ~/tensorflow/bin/activate easy_install -U pip pip install --upgrade tensorflow deactivate 图19安装TensorFlow界面 1.2.4Python类库 安装Python的相关类库,输入如下命令: pip install numpy pip install pandas pip install matplotlib sudo pip install keras sudo pip install music21 sudo pip install h5py sudo apt install ffmpeg sudo apt install timidity 1.3模块实现 本部分包括数据准备、信息提取、模型构建、模型训练及保存、音乐模块,下面分别给出各模块的功能介绍及相关代码。 1.3.1数据准备 数据来自互联网下载的70首音乐文件,格式为MIDI,相关数据见“数据集文件11”,如图110所示。 图110训练数据集 1.3.2信息提取 数据准备完成后,需要进行文件格式转换及音乐信息提取。 1. 文件格式转换 使用Timidity软件,实现MIDI转换为MP3等其他流媒体格式的操作。相关代码见“代码文件11”。 2. 音乐信息提取 将MIDI文件中的音符数据全部提取,包括note和chord的处理; note指音符,而chord指和弦。使用的软件是Music21,它可以对MIDI文件进行一些数据提取或者写入,相关代码如下。 import os from music21 import converter, instrument def print_notes(): if not os.path.exists("1.mid"): raise Exception("MIDI 文件 1.mid 不在此目录下,请添加") #读取MIDI文件,输出Stream 流类型 stream = converter.parse("1.mid") #解析1.mid的内容 #获得所有乐器部分 parts = instrument.partitionByInstrument(stream) if parts: #如果有乐器部分,取第一个乐器部分,先采取一个音轨 notes = parts.parts[0].recurse() #递归获取 else: notes = stream.flat.notes #输出每个元素 for element in notes: print(str(element)) if __name__ == "__main__": print_notes() 1.3.3模型构建 数据加载后,需要进行定义结构并优化损失函数。 1. 定义结构 图111为图形化的神经网络搭建模型,共9层,只用LSTM的70%,舍弃30%,这是为了防止过拟合,最后全连接层的音调数就是初始定义num_pitch的数目,用神经网络去预测每次生成的新音调属于所有音调中的哪一个,利用交叉熵和Softmax(激活层)计算出概率最高那个作为输出(输出为预测音调对应的序列)。还需要在代码后面添加指定模型的损失函数并进行优化器设置。 图111神经网络模型结构 #RNN-LSTM 循环神经网络 import tensorflow as tf #神经网络模型 def network_model(inputs, num_pitch, weights_file=None): model = tf.keras.models.Sequential() 构建一个神经网络模型(其中Sequential是序列的意思),在TensorFlow官网中可以看到基本用法,通过add()函数添加需要的层。Sequential相当于一个汉堡模型,根据自己的需要按顺序填充不同层,相关代码如下。 #模型框架,第n层输出会成为第n+1层的输入,一共9层 model.add(tf.keras.layers.LSTM( 512, #LSTM 层神经元的数目是512,也是LSTM层输出的维度 input_shape=(inputs.shape[1], inputs.shape[2]), #输入的形状,对第一个LSTM层必须设置 #return_sequences: 控制返回类型 #True: 返回所有的输出序列 #False: 返回输出序列的最后一个输出 #在堆叠LSTM层时必须设置,最后一层LSTM可以不用设置 return_sequences=True #返回所有的输出序列 )) #丢弃30%神经元,防止过拟合 model.add(tf.keras.layers.Dropout(0.3)) model.add(tf.keras.layers.LSTM(512, return_sequences=True)) model.add(tf.keras.layers.Dropout(0.3)) model.add(tf.keras.layers.LSTM(512)) #return_sequences 是默认的False,只返回输出序列的最后一个 #256个神经元的全连接层 model.add(tf.keras.layers.Dense(256)) model.add(tf.keras.layers.Dropout(0.3)) model.add(tf.keras.layers.Dense(num_pitch)) #输出的数目等于所有不重复的音调数目: num_pitch 2. 优化损失函数 确定神经网络模型架构之后,需要对模型进行编译,这是回归分析问题,因此先用Softmax计算百分比概率,再用Cross entropy(交叉熵)计算概率和独热码之间的误差,使用RMSProp优化器优化模型参数,相关代码如下。 model.add(tf.keras.layers.Activation('softmax')) #Softmax激活函数计算概率 #交叉熵计算误差,使用RMSProp优化器 #计算误差 model.compile(loss='categorical_crossentropy', optimizer='rmsprop') #损失函数loss,优化器optimizer if weights_file is not None: #如果是生成音乐 #从HDF5文件中加载所有神经网络层的参数 model.load_weights(weights_file) return model 1.3.4模型训练及保存 构建完整模型后,在训练模型之前需要准备输入序列。创建一个字典,用于映射音调和整数,同时还需要通过字典将整数映射成音调。除此之外,将输入序列的形状转换成神经网络模型可接收的形式,输入归一化。前面在构建神经网络模型时定义损失函数,用布尔的形式计算交叉熵,所以要将期望输出转换成0和1组成的布尔矩阵。 1. 模型训练 模型训练相关代码如下。 import numpy as np import tensorflow as tf from utils import * from network import * #训练神经网络 def train(): notes = get_notes() #得到所有不重复的音调数目 num_pitch = len(set(notes)) network_input, network_output = prepare_sequences(notes, num_pitch) model = network_model(network_input, num_pitch) filepath = "weights-{epoch:02d}-{loss:.4f}.hdf5" 在训练模型之前,需要定义一个检查点,其目的是在每轮结束时保存模型参数(weights),在训练过程中不会丢失模型参数,而且在对损失满意时随时停止训练。根据官方文件提供的示例格式设置文件路径,不断更新保存模型参数weights,格式为.hdf5。其中checkpoint中参数设置save_best_only=True,是指监视器monitor=“loss”监视保存最好的损失,如果这次损失比上次损失小,则上次参数就会被覆盖,相关代码如下。 checkpoint = tf.keras.callbacks.ModelCheckpoint( filepath, #保存的文件路径 monitor='loss', #监控的对象是损失(loss) verbose=0, save_best_only=True, mode='min' #取损失最小的 ) callbacks_list = [checkpoint] #用fit()函数训练模型 model.fit(network_input, network_output, epochs=100, batch_size=64, callbacks=callbacks_list) #为神经网络准备好训练的序列 def prepare_sequences(notes, num_pitch): sequence_length = 100 #序列长度 #得到所有不重复音调的名字 pitch_names = sorted(set(item for item in notes)) #sorted用于字母排序 #创建一个字典,用于映射音调和整数 pitch_to_int=dict((pitch,num) for num,pitch in enumerate(pitch_names) #enumerate是枚举 #创建神经网络的输入序列和输出序列 network_input = [] network_output = [] for i in range(0, len(notes) - sequence_length, 1): #每隔一个音符就取前面的一百个音符用来训练 sequence_in = notes[i: i + sequence_length] sequence_out = notes[i + sequence_length] network_input.append([pitch_to_int[char] for char in sequence_in]) Batch size是批次(样本)数目,它是一次迭代所用的样本数目。Iteration是迭代,每次迭代更新一次权重(网络参数),每次权重更新需要Batch size个数据前向运算后再进行反向运算,一个Epoch指所有的训练样本完成一次迭代。 2. 模型保存 训练神经网络后,将weights参数保存在HDF5文件中,相关代码如下。 #将sequence_in中的每个字符转换成数字后存入network_input中 network_output.append(pitch_to_int[sequence_out]) n_patterns = len(network_input) #将输入的形状转换成神经网络模型可以接收的形式 network_input=np.reshape(network_input,(n_patterns,sequence_length, 1)) #输入标准化/归一化 #归一化可以使之后的优化器(optimizer)更快更好地找到误差最小值 network_input = network_input /float(num_pitch) #将期望输出转换成{0, 1}组成的布尔矩阵,配合误差算法使用 network_output = tf.keras.utils.to_categorical(network_output) return network_input, network_output if __name__ == '__main__': train() 1.3.5音乐模块 本模块主要有序列准备、音符生成和音乐生成,主要作用如下: ①为神经网络准备好供训练的序列; ②基于序列音符,用神经网络生成新的音符; ③用训练好的神经网络模型参数作曲。 在训练模型时用fit()函数,模型预测数据时用predict()函数得到最大的维度,也就是概率最高的音符。将实际预测的整数转换成音调保存,输入序列向后移动,不断生成新的音调。 1. 序列准备 序列准备相关代码如下。 def prepare_sequences(notes, pitch_names, num_pitch): #为神经网络准备好供训练的序列 sequence_length = 100 #创建一个字典,用于映射音调和整数 pitch_to_int = dict((pitch,num) for num, pitch in enumerate(pitch_names)) #创建神经网络的输入序列和输出序列 network_input = [] network_output = [] for i in range(0, len(notes) - sequence_length, 1): sequence_in = notes[i: i + sequence_length] sequence_out = notes[i + sequence_length] network_input.append([pitch_to_int[char] for char in sequence_in]) network_output.append(pitch_to_int[sequence_out]) n_patterns = len(network_input) #将输入的形状转换成神经网络模型可以接收的形式 normalized_input=np.reshape(network_input,(n_patterns sequence_length, 1)) #输入标准化/归一化 normalized_input = normalized_input / float(num_pitch) return network_input, normalized_input 2. 音符生成 音符生成相关代码如下。 def generate_notes(model, network_input, pitch_names, num_pitch): #基于序列音符,用神经网络生成新的音符 #从输入中随机选择一个序列,作为预测/生成音乐的起始点 start = np.random.randint(0, len(network_input) - 1) #创建一个字典,用于映射整数和音调 int_to_pitch = dict((num, pitch) for num, pitch in enumerate(pitch_names)) pattern = network_input[start] #神经网络实际生成的音调 prediction_output = [] #生成700个音符/音调 for note_index in range(700): prediction_input = np.reshape(pattern, (1, len(pattern), 1)) #输入归一化 prediction_input = prediction_input / float(num_pitch) #用载入了训练所得最佳参数文件的神经网络预测/生成新的音调 prediction = model.predict(prediction_input, verbose=0) #argmax取最大的维度 index = np.argmax(prediction) #将整数转成音调 result = int_to_pitch[index] prediction_output.append(result) #向后移动 pattern.append(index) pattern = pattern[1:len(pattern)] return prediction_output if __name__ == '__main__': generate() 3. 音乐生成 音乐生成相关代码如下。 #使用之前训练所得的最佳参数生成音乐 def generate(): #加载用于训练神经网络的音乐数据 with open('data/notes', 'rb') as filepath: notes = pickle.load(filepath) #得到所有音调的名字 pitch_names = sorted(set(item for item in notes)) #得到所有不重复的音调数目 num_pitch = len(set(notes)) network_input, normalized_input = prepare_sequences(notes, pitch_names, num_pitch) #载入之前训练时最好的参数文件,生成神经网络模型 model = network_model(normalized_input, num_pitch, "best-weights.hdf5") #用神经网络生成音乐数据 prediction=generate_notes(model, network_input, pitch_names, num_pitch) #用预测的音乐数据生成MIDI文件,再转换成MP3格式 create_music(prediction) 1.4系统测试 本部分包括训练过程及测试效果。 1.4.1训练过程 运行python train.py并开始训练,默认训练100个epoch,可使用组合键Ctrl+C结束训练,如图112所示。 图112训练过程 随着epoch增加,损失率越来越低,模型在训练数据、测试数据上的损失和准确率逐渐收敛,最终趋于稳定。 生成MP3格式的音乐时,先从output.mid生成MIDI文件,再从output.mid生成output.mp3文件——确保其位于generate.py同级目录下,运行python generate.py即可生成MP3格式的音乐。 1.4.2测试效果 生成结果如图113所示,output.mid是直接生成的MIDI文件,output.mp3是转换后的MP3流媒体格式文件。 图113生成的MIDI文件和MP3文件 通过Garage Band尝试播放生成的音乐,如图114所示。 图114播放output.mid 项目2 语 音 识 别 本项目基于卷积神经网络(Convolutional Neural Networks,CNN)对采样音频不同的声谱图进行识别,实现辨别数字声音。 2.1总体设计 本部分包括整体框架和系统流程。 2.1.1整体框架 整体框架如图21所示。 图21整体框架 2.1.2系统流程 系统流程如图22所示。 图22系统流程 2.2运行环境 本部分包括Python环境、PyCharm环境、PyTorch环境、CUDA和cuDNN环境。 2.2.1Python环境 在Windows环境下下载Anaconda,完成Python的环境配置,如图13所示。 2.2.2PyCharm环境 PyCharm需要在Python下载安装成功后再进行安装,如图23所示。 图23下载PyCharm界面 单击安装程序后选择路径配置相关环境,勾选所有选项后完成下载。选择项目所在路径→Previouslyconfiguredinterpreter→Createamain.py→Create进行项目搭建。右击main.py,单击运行按钮,运行成功则代表环境配置成功。 2.2.3PyTorch环境 首先,打开Anaconda命令提示行; 其次,打开Anaconda Prompt,前面显示(base)说明已经进入Anaconda的基础环境; 最后,输入相关代码。 condacreate-npytorch1_11python=3.7 pytorch1_11是创建环境的名称。python=3.7是Python的版本。 选择与自己版本对应的命令行输入即可。Package表示安装方式,在Windows下使用Conda即可,也可以选择pip虚拟指令对所有的第三方软件进行统一安装。 2.2.4CUDA和cuDNN环境 检查计算机所支持的下载版本,如图24所示,在CUDA Toolkit 12.1.1(April 2023)中右击选择nvidia→系统信息→组件。临时解压路径,建议默认即可,也可以自定义。安装结束后,临时解压文件夹会自动删除; 选择自定义安装,安装完成后配置CUDA的环境变量; 在命令行中,测试是否安装成功; 双击.exe文件,选择下载路径(推荐默认路径)。 图24安装CUDA界面 cuDNN作为CUDA的补丁环境,需要用到与CUDA相匹配的11.3版本,如图25所示。 图25安装cuDNN界面 2.2.5网页端配置环境 通过配置静态服务器端代码,将后端的结果展示于前端。如果请求的资源是HTML文件,可以认为是动态资源请求,需要将请求封装成WSGI协议要求的格式,并传输到Web框架处理。WSGI协议要求服务器端将请求报文的各项信息组合成一个字典environ,同样传输到Web框架,相关代码如下。 importsocket importthreading classMyWebServer(object): def__init__(self,port): #初始化: 创建套接字 self.server_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM) self.server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True) self.server_socket.bind(("localhost",port)) self.server_socket.listen(128) defstart(self): #启动: 建立连接,并开启子线程 whileTrue: new_socket,address=self.server_socket.accept() print("已连接",address,sep="from") sub_thread=threading.Thread(target=self.handle,args=(new_socket,),daemon=True) sub_thread.start() @staticmethod defhandle(handle_socket): #利用子线程收发HTTP格式数据 request_data=handle_socket.recv(4096) iflen(request_data)==0: print("浏览器已断开连接...") handle_socket.close() return request_content=request_data.decode("utf-8") print(request_content) #以\r\n分割各项信息 request_list=request_content.split("\r\n") #提取请求的资源路径 request_line=request_list[0] request_line_list=request_line.split("") request_method,request_path,request_version=request_line_list #首页 ifrequest_path=="/": request_path="/index.html" #响应行与响应头信息置空 response_line=response_header="" #根据请求路径准备好响应行和响应体 try: withopen("."+request_path,"rb")asrequest_file: response_body=request_file.read() except(FileExistsError,FileNotFoundError): response_line+=f"{request_version}404NotFound\r\n" withopen("./error.html","rb")asrequest_file: response_body=request_file.read() else: response_line+=f"{request_version}200OK\r\n" finally: #准备好响应头信息 response_header+="Server:MyWebServer2.0\r\n" #向浏览器发送响应报文 response_data=(response_line+response_header+"\r\n").encode("utf-8")+response_body handle_socket.send(response_data) #断开与浏览器的连接 handle_socket.close() defmain(): port=8888 my_web_server=MyWebServer(port) my_web_server.start() if__name__=="__main__": main() 2.3模块实现 本部分包括数据准备、模型构建、模型训练及保存、模型应用,下面分别给出各模块的功能介绍及相关代码。 2.3.1数据准备 数据集使用speech_commands,speech_commands的1.1v数据集包含500ms的录音/波形,其中有一些随机的数字音频或者空白的音频。数据集界面如图26所示。每个波形的标签是文件名的最后一个数字(0~9), 图26数据集界面