神经网络的频率原则:先学习低频,再学习高频
2026/5/26 14:42:55 网站建设 项目流程

神经网络模型拟合曲线,先拟合低频信号,再拟合高频信号,以下是验证代码。这个代码实现了以下功能:

  • 可视化功能

    • 每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(fname=font_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_ok=True) os.makedirs('output', exist_ok=True) # 3. 生成数据 - 修改为 y = sin(2πx) + sin(20πx) + sin(40πx) def generate_data(n_samples=1000): """生成训练数据: 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_size=128): 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_rate=100): """计算信号的频域表示""" 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-', linewidth=2, label='原始信号', alpha=0.7) ax.plot(freq_pred, mag_pred, 'r--', linewidth=2, label='拟合信号', alpha=0.9) ax.set_xlabel('频率 (Hz)', fontsize=12) ax.set_ylabel('幅度', fontsize=12) ax.set_title(f'频域对比 (Epoch: {epoch})', fontsize=14, pad=15) ax.legend(fontsize=10) ax.grid(True, alpha=0.3) ax.set_xlim([0, 50]) # 调整为更高的频率范围 ax.set_ylim(bottom=0) ax.tick_params(axis='both', which='major', labelsize=10) return ax # 8. 增强的训练函数 def train_model(): # 参数设置 epochs = 10000 save_interval = 500 learning_rate = 0.001 # 降低学习率以适应更复杂的信号 # 生成数据 x, y_true = generate_data(2000) # 增加采样点 # 初始化模型 model = SignalNet(hidden_size=256) # 增加隐藏层大小 criterion = nn.MSELoss() optimizer = optim.Adam(model.parameters(), lr=learning_rate) # 学习率调度器 scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=2000, gamma=0.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(f'Epoch [{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)', fontsize=20, y=0.98, fontweight='bold') # 时域图 ax1 = axes[0] ax1.plot(x_np, y_true_np, 'b-', label='原始信号', alpha=0.7, linewidth=1.5) ax1.plot(x_np, y_pred_np, 'r--', label='拟合信号', alpha=0.9, linewidth=1.5) ax1.set_xlabel('x', fontsize=14) ax1.set_ylabel('y', fontsize=14) ax1.set_title(f'时域对比 (Epoch: {epoch})', fontsize=16, pad=15) ax1.legend(fontsize=12, loc='upper right') ax1.grid(True, alpha=0.3) ax1.set_xlim([0, 2]) ax1.tick_params(axis='both', which='major', labelsize=12) # 频域图 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(x=freq, color=colors[i], linestyle=':', alpha=0.5, linewidth=1) ax2.text(freq+0.5, ax2.get_ylim()[1]*0.9, f'{freq}Hz', color=colors[i], fontsize=10, alpha=0.7) # 在图中添加损失信息 plt.figtext(0.5, 0.01, f'Epoch: {epoch}, Loss: {loss.item():.6f}, Learning Rate: {scheduler.get_last_lr()[0]:.6f}', ha='center', fontsize=12, bbox=dict(boxstyle="round,pad=0.3", facecolor='lightgray', alpha=0.7)) # 调整布局 plt.tight_layout(rect=[0, 0.05, 1, 0.95]) # 保存图片 filename = f'training_figures/epoch_{epoch:05d}.png' plt.savefig(filename, dpi=150, bbox_inches='tight') 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 = f'training_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, width=width, height=height) # 添加底部说明 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 = f"Epoch {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(f"PPT已保存: {ppt_path}") print(f"PPT包含 {len(prs.slides)} 张幻灯片") return ppt_path # 11. 生成训练报告 def generate_report(losses, sampling_rate): """生成训练报告""" report_path = 'output/training_report.txt' with open(report_path, 'w', encoding='utf-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(f"CUDA可用,使用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, alpha=0.3) plt.subplot(1, 2, 2) plt.semilogy(losses) # 使用对数坐标 plt.xlabel('Epoch') plt.ylabel('Loss (log scale)') plt.title('训练损失曲线(对数坐标)') plt.grid(True, alpha=0.3) plt.suptitle('训练损失曲线', fontsize=16, fontweight='bold') plt.tight_layout() plt.savefig('output/loss_curve.png', dpi=150, bbox_inches='tight') # 绘制最终结果 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='原始信号', alpha=0.7, linewidth=1.5) axes[0].plot(x_np, y_pred_np, 'r--', label='拟合信号', alpha=0.9, linewidth=1.5) axes[0].set_xlabel('x', fontsize=14) axes[0].set_ylabel('y', fontsize=14) axes[0].set_title(f'最终拟合结果 (Epoch: 10000)', fontsize=16, pad=15) axes[0].legend(fontsize=12, loc='upper right') axes[0].grid(True, alpha=0.3) axes[0].set_xlim([0, 2]) axes[0].tick_params(axis='both', which='major', labelsize=12) # 频域图 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}', fontsize=20, y=0.98, fontweight='bold') plt.tight_layout(rect=[0, 0, 1, 0.95]) plt.savefig('output/final_result.png', dpi=150, bbox_inches='tight') 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()

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询