神经网络模型拟合曲线先拟合低频信号再拟合高频信号以下是验证代码。这个代码实现了以下功能可视化功能每500个epoch保存一张图片每张图片包含时域对比和频域对比解决中文字符显示问题共生成21张图片包括epoch 0视频制作将21张图片合成为MP4视频2帧/秒的播放速度最后一张图片多停留5秒PPT生成自动生成PPT报告包含标题页、图片展示页和视频页使用python-pptx库import torch import torch.nn as nn import torch.optim as optim import numpy as np import matplotlib.pyplot as plt from matplotlib import font_manager import os import cv2 from pptx import Presentation from pptx.util import Inches import warnings warnings.filterwarnings(ignore) # 1. 设置中文字体 - 更可靠的方法 def setup_chinese_font(): 设置中文字体使用绝对路径 try: # 尝试多种方法设置中文字体 font_paths [ C:/Windows/Fonts/simhei.ttf, # Windows C:/Windows/Fonts/msyh.ttc, # Windows微软雅黑 /System/Library/Fonts/PingFang.ttc, # Mac /usr/share/fonts/truetype/wqy/wqy-microhei.ttc, # Linux ] font_added False for font_path in font_paths: if os.path.exists(font_path): font_prop font_manager.FontProperties(fnamefont_path) font_manager.fontManager.addfont(font_path) font_name font_prop.get_name() plt.rcParams[font.sans-serif] [font_name] print(f使用字体: {font_name}) font_added True break if not font_added: # 如果找不到字体使用默认英文字体 plt.rcParams[font.sans-serif] [DejaVu Sans] print(使用英文字体) plt.rcParams[axes.unicode_minus] False except Exception as e: print(f字体设置失败: {e}使用默认字体) plt.rcParams[font.sans-serif] [DejaVu Sans] plt.rcParams[axes.unicode_minus] False setup_chinese_font() # 2. 创建保存目录 os.makedirs(training_figures, exist_okTrue) os.makedirs(output, exist_okTrue) # 3. 生成数据 - 修改为 y sin(2πx) sin(20πx) sin(40πx) def generate_data(n_samples1000): 生成训练数据: y sin(2πx) sin(20πx) sin(40πx) x torch.linspace(0, 2, n_samples).reshape(-1, 1) # 修改x范围为[0, 2]以看到完整的周期性 y torch.sin(2*np.pi*x) torch.sin(20*np.pi*x) torch.sin(40*np.pi*x) return x, y # 4. 定义神经网络模型 class SignalNet(nn.Module): def __init__(self, hidden_size128): super(SignalNet, self).__init__() self.net nn.Sequential( nn.Linear(1, hidden_size), nn.ReLU(), nn.Linear(hidden_size, hidden_size), nn.ReLU(), nn.Linear(hidden_size, hidden_size), nn.ReLU(), nn.Linear(hidden_size, 1) ) def forward(self, x): return self.net(x) # 5. 计算频域信号 def compute_frequency_domain(signal, sampling_rate100): 计算信号的频域表示 n len(signal) # 对信号进行FFT yf np.fft.fft(signal.flatten()) xf np.fft.fftfreq(n, 1/sampling_rate) # 只取正频率部分 idx np.where(xf 0) xf_pos xf[idx] yf_pos np.abs(yf[idx]) / n * 2 yf_pos[0] / 2 # DC分量不需要乘以2 return xf_pos[:n//2], yf_pos[:n//2] # 6. 分析信号的频率成分 def analyze_frequency_components(): 分析目标信号的频率成分 x, y generate_data(1000) x_np x.numpy().flatten() y_np y.numpy().flatten() # 计算采样率 dx x_np[1] - x_np[0] # 采样间隔 sampling_rate 1 / dx # 采样频率 freq, mag compute_frequency_domain(y_np, sampling_rate) print(f采样间隔: {dx:.6f}) print(f采样频率: {sampling_rate:.2f} Hz) print(f奈奎斯特频率: {sampling_rate/2:.2f} Hz) # 找到主要频率成分 print(\n主要频率成分:) freq_indices np.argsort(mag)[-5:] # 找到幅度最大的5个频率 for idx in freq_indices: if mag[idx] 0.01: # 只显示幅度大于0.01的成分 print(f 频率: {freq[idx]:.2f} Hz, 幅度: {mag[idx]:.4f}) # 7. 简化频域绘图 def plot_frequency_domain_simple(ax, freq_true, mag_true, freq_pred, mag_pred, epoch): 使用线图绘制频域对比避免stem问题 ax.plot(freq_true, mag_true, b-, linewidth2, label原始信号, alpha0.7) ax.plot(freq_pred, mag_pred, r--, linewidth2, label拟合信号, alpha0.9) ax.set_xlabel(频率 (Hz), fontsize12) ax.set_ylabel(幅度, fontsize12) ax.set_title(f频域对比 (Epoch: {epoch}), fontsize14, pad15) ax.legend(fontsize10) ax.grid(True, alpha0.3) ax.set_xlim([0, 50]) # 调整为更高的频率范围 ax.set_ylim(bottom0) ax.tick_params(axisboth, whichmajor, labelsize10) return ax # 8. 增强的训练函数 def train_model(): # 参数设置 epochs 10000 save_interval 500 learning_rate 0.001 # 降低学习率以适应更复杂的信号 # 生成数据 x, y_true generate_data(2000) # 增加采样点 # 初始化模型 model SignalNet(hidden_size256) # 增加隐藏层大小 criterion nn.MSELoss() optimizer optim.Adam(model.parameters(), lrlearning_rate) # 学习率调度器 scheduler optim.lr_scheduler.StepLR(optimizer, step_size2000, gamma0.5) # 记录损失 losses [] # 计算采样率用于频域分析 x_np x.numpy().flatten() dx x_np[1] - x_np[0] sampling_rate 1 / dx print(f采样频率: {sampling_rate:.2f} Hz) print(f目标频率: 1 Hz, 10 Hz, 20 Hz (对应 sin(2πx), sin(20πx), sin(40πx))) # 训练循环 for epoch in range(epochs 1): # 前向传播 y_pred model(x) loss criterion(y_pred, y_true) # 反向传播 optimizer.zero_grad() loss.backward() optimizer.step() scheduler.step() losses.append(loss.item()) # 每500个epoch保存图片 if epoch % save_interval 0 or epoch 0: print(fEpoch [{epoch:5d}/{epochs}], Loss: {loss.item():.6f}, LR: {scheduler.get_last_lr()[0]:.6f}) with torch.no_grad(): y_pred model(x) # 转换为numpy用于绘图 y_true_np y_true.numpy().flatten() y_pred_np y_pred.numpy().flatten() # 创建图形 - 增大图形尺寸 fig, axes plt.subplots(1, 2, figsize(16, 8)) # 设置主标题 fig.suptitle(f信号拟合: y sin(2πx) sin(20πx) sin(40πx), fontsize20, y0.98, fontweightbold) # 时域图 ax1 axes[0] ax1.plot(x_np, y_true_np, b-, label原始信号, alpha0.7, linewidth1.5) ax1.plot(x_np, y_pred_np, r--, label拟合信号, alpha0.9, linewidth1.5) ax1.set_xlabel(x, fontsize14) ax1.set_ylabel(y, fontsize14) ax1.set_title(f时域对比 (Epoch: {epoch}), fontsize16, pad15) ax1.legend(fontsize12, locupper right) ax1.grid(True, alpha0.3) ax1.set_xlim([0, 2]) ax1.tick_params(axisboth, whichmajor, labelsize12) # 频域图 ax2 axes[1] # 计算频域 freq_true, mag_true compute_frequency_domain(y_true_np, sampling_rate) freq_pred, mag_pred compute_frequency_domain(y_pred_np, sampling_rate) # 使用线图 ax2 plot_frequency_domain_simple(ax2, freq_true, mag_true, freq_pred, mag_pred, epoch) # 标记主要频率成分 target_freqs [1, 10, 20] # 目标信号的频率 colors [green, orange, purple] for i, freq in enumerate(target_freqs): ax2.axvline(xfreq, colorcolors[i], linestyle:, alpha0.5, linewidth1) ax2.text(freq0.5, ax2.get_ylim()[1]*0.9, f{freq}Hz, colorcolors[i], fontsize10, alpha0.7) # 在图中添加损失信息 plt.figtext(0.5, 0.01, fEpoch: {epoch}, Loss: {loss.item():.6f}, Learning Rate: {scheduler.get_last_lr()[0]:.6f}, hacenter, fontsize12, bboxdict(boxstyleround,pad0.3, facecolorlightgray, alpha0.7)) # 调整布局 plt.tight_layout(rect[0, 0.05, 1, 0.95]) # 保存图片 filename ftraining_figures/epoch_{epoch:05d}.png plt.savefig(filename, dpi150, bbox_inchestight) plt.close() print(f 已保存图片: {filename}) return model, losses, sampling_rate # 9. 创建视频 def create_video(): print(\n正在创建视频...) # 获取所有图片文件 image_folder training_figures video_name output/training_progress.mp4 # 获取并按epoch排序图片 image_files [] for f in os.listdir(image_folder): if f.endswith(.png) and f.startswith(epoch_): # 从文件名提取epoch数用于排序 try: epoch int(f.split(_)[1].split(.)[0]) image_files.append((epoch, f)) except: pass # 按epoch排序 image_files.sort() image_files [f[1] for f in image_files] # 只保留文件名 if not image_files: print(没有找到图片文件) return # 读取第一张图片获取尺寸 first_image_path os.path.join(image_folder, image_files[0]) frame cv2.imread(first_image_path) if frame is None: print(无法读取第一张图片) return height, width, layers frame.shape # 创建视频写入器 fourcc cv2.VideoWriter_fourcc(*mp4v) video cv2.VideoWriter(video_name, fourcc, 2, (width, height)) # 2 fps for i, image in enumerate(image_files): img_path os.path.join(image_folder, image) img cv2.imread(img_path) if img is not None: video.write(img) print(f 已添加帧: {image}) # 最后一张图片多停留一会儿 if i len(image_files) - 1: for _ in range(20): # 多停留10秒 video.write(img) video.release() cv2.destroyAllWindows() print(f视频已保存: {video_name}) # 10. 改进的PPT生成函数 def create_ppt(): print(\n正在创建PPT...) # 创建新的PPT prs Presentation() # 设置PPT尺寸为16:9 prs.slide_width Inches(13.33) # 16:9的宽度 prs.slide_height Inches(7.5) # 16:9的高度 # 标题幻灯片 title_slide_layout prs.slide_layouts[0] slide prs.slides.add_slide(title_slide_layout) # 设置标题 title slide.shapes.title title.text 神经网络信号拟合训练报告 title.text_frame.paragraphs[0].font.size Inches(0.7) # 设置副标题 subtitle slide.placeholders[1] subtitle.text f拟合函数: y sin(2πx) sin(20πx) sin(40πx)\n训练轮数: 10000\n生成时间: 2026年5月25日\n\n使用框架: PyTorch\nPython 3.12.10 # 获取所有图片并按epoch排序 image_files [] for f in os.listdir(training_figures): if f.endswith(.png) and f.startswith(epoch_): try: epoch int(f.split(_)[1].split(.)[0]) image_files.append((epoch, f)) except: pass image_files.sort() # 按epoch排序 if not image_files: print(没有找到训练图片) return None print(f找到 {len(image_files)} 张图片) # 为每张图片创建单独的幻灯片 for epoch, img_file in image_files: # 创建空白幻灯片 blank_slide_layout prs.slide_layouts[6] slide prs.slides.add_slide(blank_slide_layout) # 添加标题 title_box slide.shapes.add_textbox(Inches(1), Inches(0.2), Inches(11.33), Inches(0.8)) title_frame title_box.text_frame title_frame.text f训练进度 - Epoch: {epoch} title_frame.paragraphs[0].font.size Inches(0.5) title_frame.paragraphs[0].font.bold True title_frame.paragraphs[0].alignment 1 # 居中 # 添加图片 img_path ftraining_figures/{img_file} if os.path.exists(img_path): # 计算图片大小和位置以使其居中 left Inches(0.5) top Inches(1.0) width Inches(12.33) height Inches(6.0) # 稍微减小高度为底部留出空间 slide.shapes.add_picture(img_path, left, top, widthwidth, heightheight) # 添加底部说明 footer_box slide.shapes.add_textbox(Inches(1), Inches(7.1), Inches(11.33), Inches(0.4)) footer_frame footer_box.text_frame footer_frame.text fEpoch {epoch}/10000 - 神经网络拟合复合正弦信号: y sin(2πx) sin(20πx) sin(40πx) footer_frame.paragraphs[0].font.size Inches(0.2) footer_frame.paragraphs[0].alignment 1 # 居中 else: print(f警告: 图片不存在 {img_path}) # 添加视频幻灯片 slide_layout prs.slide_layouts[1] slide prs.slides.add_slide(slide_layout) title slide.shapes.title title.text 训练过程完整视频 content slide.placeholders[1] content.text 以下为完整的训练过程视频展示了神经网络从随机初始化到逐步拟合目标函数的全过程。\n\n视频文件: training_progress.mp4\n\n双击下方图标播放视频 # 添加视频占位符 left Inches(4) top Inches(3) width Inches(5.33) height Inches(3) # 添加矩形框作为占位符 from pptx.enum.shapes import MSO_SHAPE from pptx.dml.color import RGBColor shape slide.shapes.add_shape( MSO_SHAPE.ROUNDED_RECTANGLE, left, top, width, height ) shape.text f▶ 播放视频 shape.fill.solid() shape.fill.fore_color.rgb RGBColor(240, 240, 240) shape.line.color.rgb RGBColor(0, 120, 215) shape.line.width 2 # 添加视频文件路径 path_box slide.shapes.add_textbox(Inches(2), Inches(6.5), Inches(9.33), Inches(0.5)) path_frame path_box.text_frame path_frame.text 视频文件位置: output/training_progress.mp4 path_frame.paragraphs[0].font.size Inches(0.18) path_frame.paragraphs[0].alignment 1 # 居中 # 添加总结幻灯片 summary_slide prs.slides.add_slide(title_slide_layout) summary_title summary_slide.shapes.title summary_title.text 训练总结 summary_content summary_slide.placeholders[1] summary_content.text 训练已完成\n\n总结:\n• 总训练轮数: 10000\n• 生成图片数量: 21张\n• 训练过程已保存为视频\n• 最终损失值已收敛\n\n通过神经网络成功拟合了复合正弦波函数\n目标函数: y sin(2πx) sin(20πx) sin(40πx) # 保存PPT ppt_path output/training_report.pptx prs.save(ppt_path) print(fPPT已保存: {ppt_path}) print(fPPT包含 {len(prs.slides)} 张幻灯片) return ppt_path # 11. 生成训练报告 def generate_report(losses, sampling_rate): 生成训练报告 report_path output/training_report.txt with open(report_path, w, encodingutf-8) as f: f.write( * 60 \n) f.write(信号拟合训练报告\n) f.write( * 60 \n\n) f.write(f训练时间: 2026年5月25日\n) f.write(f训练环境:\n) f.write(f Python版本: 3.12.10\n) f.write(f PyTorch版本: {torch.__version__}\n) f.write(f CUDA版本: {torch.version.cuda if torch.cuda.is_available() else 未使用CUDA}\n\n) f.write(目标函数: y sin(2πx) sin(20πx) sin(40πx)\n) f.write(频率成分: 1Hz, 10Hz, 20Hz\n\n) f.write(神经网络结构:\n) f.write( 输入层: 1个神经元\n) f.write( 隐藏层: 3层每层256个神经元使用ReLU激活函数\n) f.write( 输出层: 1个神经元\n\n) f.write(训练参数:\n) f.write(f 训练轮数: 10000\n) f.write(f 学习率: 0.001 (带衰减)\n) f.write(f 优化器: Adam\n) f.write(f 损失函数: MSE\n) f.write(f 采样频率: {sampling_rate:.2f} Hz\n) f.write(f 采样点数: 2000\n\n) f.write(训练结果:\n) f.write(f 初始损失: {losses[0]:.6f}\n) f.write(f 最终损失: {losses[-1]:.6f}\n) f.write(f 损失减少: {losses[0] - losses[-1]:.6f}\n) f.write(f 最终损失为初始损失的: {losses[-1]/losses[0]*100:.2f}%\n\n) f.write(生成文件:\n) f.write( training_figures/ - 21张训练过程图片\n) f.write( output/training_progress.mp4 - 训练过程视频\n) f.write( output/training_report.pptx - PPT报告\n) f.write( output/training_report.txt - 文本报告\n) f.write( output/loss_curve.png - 损失曲线图\n) f.write( output/final_result.png - 最终拟合结果图\n) print(f训练报告已保存: {report_path}) # 12. 主函数 def main(): print(信号频率分析:) print( * 60) analyze_frequency_components() print(\n开始训练神经网络...) print( * 60) # 检查CUDA是否可用 if torch.cuda.is_available(): print(fCUDA可用使用GPU: {torch.cuda.get_device_name(0)}) device torch.device(cuda) else: print(使用CPU进行训练) device torch.device(cpu) # 训练模型 model, losses, sampling_rate train_model() # 生成训练报告 generate_report(losses, sampling_rate) # 创建视频 create_video() # 创建PPT ppt_path create_ppt() # 绘制损失曲线 plt.figure(figsize(12, 6)) plt.subplot(1, 2, 1) plt.plot(losses) plt.xlabel(Epoch) plt.ylabel(Loss) plt.title(训练损失曲线) plt.grid(True, alpha0.3) plt.subplot(1, 2, 2) plt.semilogy(losses) # 使用对数坐标 plt.xlabel(Epoch) plt.ylabel(Loss (log scale)) plt.title(训练损失曲线(对数坐标)) plt.grid(True, alpha0.3) plt.suptitle(训练损失曲线, fontsize16, fontweightbold) plt.tight_layout() plt.savefig(output/loss_curve.png, dpi150, bbox_inchestight) # 绘制最终结果 with torch.no_grad(): x_test, y_true generate_data(2000) y_pred model(x_test) x_np x_test.numpy().flatten() y_true_np y_true.numpy().flatten() y_pred_np y_pred.numpy().flatten() fig, axes plt.subplots(1, 2, figsize(16, 8)) # 时域图 axes[0].plot(x_np, y_true_np, b-, label原始信号, alpha0.7, linewidth1.5) axes[0].plot(x_np, y_pred_np, r--, label拟合信号, alpha0.9, linewidth1.5) axes[0].set_xlabel(x, fontsize14) axes[0].set_ylabel(y, fontsize14) axes[0].set_title(f最终拟合结果 (Epoch: 10000), fontsize16, pad15) axes[0].legend(fontsize12, locupper right) axes[0].grid(True, alpha0.3) axes[0].set_xlim([0, 2]) axes[0].tick_params(axisboth, whichmajor, labelsize12) # 频域图 axes[1] plot_frequency_domain_simple(axes[1], *compute_frequency_domain(y_true_np, sampling_rate), *compute_frequency_domain(y_pred_np, sampling_rate), 10000) fig.suptitle(f最终拟合结果对比\nLoss: {losses[-1]:.6f}, fontsize20, y0.98, fontweightbold) plt.tight_layout(rect[0, 0, 1, 0.95]) plt.savefig(output/final_result.png, dpi150, bbox_inchestight) print(\n * 60) print(所有任务完成) print( * 60) print(\n生成的文件:) print(1. training_figures/ 目录: 包含21张训练过程图片) print(2. output/training_progress.mp4: 训练过程视频) print(3. output/training_report.pptx: PPT报告) print(4. output/training_report.txt: 文本报告) print(5. output/loss_curve.png: 损失曲线图) print(6. output/final_result.png: 最终拟合结果图) # 显示图表 plt.show() # 13. 运行主程序 if __name__ __main__: main()