尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

论文复现代码指南:打造机器学习可复现项目的实践方法

发布时间:2026/9/25 2:09:03

资讯中心
01
ARTICLE

论文复现代码指南:打造机器学习可复现项目的实践方法

论文复现代码指南:打造机器学习可复现项目的实践方法
简介一套面向计算机专业学生的高分机器学习大作业复现代码源自导师指导下的毕业设计项目评审得分98分。资源聚焦神经对话生成中的对抗学习包含生成模型、判别模型、预训练与训练测试等完整流程适合课程设计、期末大作业或算法实战练习。压缩包共20个文件以Python脚本为主辅以XML工程配置、说明文档PDF、README及项目文件整体仅570KB轻量但结构完整。已有278人学习下载。代码涵盖数据生成、模型搭建、对抗训练、评估测试等环节使用者可对照论文复现实验也可在此基础上调整参数或扩展思路。资源附有说明文档便于理解项目背景与运行步骤对有机器学习基础、希望快速获取可运行项目样板的学生尤为实用。1. 论文复现代码不是能跑就行一份高分作业该有的可复现性不少同学拿到论文复现代码第一反应都是先把训练跑通看到 loss 下降就松一口气再截一张训练曲线图交给老师。等到答辩时被问一句你复现的指标和论文差多少为什么差有哪些参数是你自己试出来的才发现除了能跑什么都答不上来。这篇笔记想解决的就是这个问题怎么把一份机器学习高分作业意义上的论文复现代码组织成可以直接使用、可以反复复现、可以解释清楚的东西。我会用 DeepWalk 这篇图表示学习论文作为贯穿例子因为它的原理直观、代码量适中很适合作为复现起步。2. 从论文到代码拆解一篇机器学习论文的复现路线复现论文最忌讳上来就写代码。我一般先花半小时把论文的实验部分翻透因为实验部分定义了复现成功到底是什么意思。代码错了可以改目标错了后面会一直往错的发现走。这里的关键是把两件事搞清楚实验边界和数据流。2.1 先界定实验边界数据集、指标、baseline以 DeepWalk 为例这篇论文要证明的是用随机游走生成的节点序列训练 Skip-gram得到的节点向量能很好地完成节点分类任务。复现前要确认四件事数据集是什么、任务是什么、指标是什么、基线方法是谁。数据集论文里常用 BlogCatalog、Flickr 这类社交网络图也有用 Wikipedia 语料做对比的。任务多标签节点分类用一部分节点训练逻辑回归再在剩余节点上评估。指标一般看 macro-F1 和 micro-F1两者计数方式不同。baseline论文会对比谱聚类、普通图嵌入等方法。这些信息不在摘要里而在 Experiments 和 Implementation Details 部分。如果论文有附录超参数表多半在附录里找不到时再看作者有没有放开源代码的配置而不是凭空猜。公式描述的是理想情况实验描述的是实际情况。很多论文的公式里省略了归一化、dropout、学习率衰减等实现细节这些只能在实验部分或开源代码里找到。所以我的顺序是先花 10 分钟看实验图表再花 5 分钟找超参数表最后才回去读方法。我会把这些问题整理成一张表直接写进作业报告里实验要素论文中的常见设置我在复现时采用的设置数据集BlogCatalog / PPI本地样例图先用小图跑通任务多标签节点分类同上数据划分随机取 10%/50% 训练固定随机种子9:1 划分评估指标macro-F1 / micro-F1macro-F1附带 micro-F1基线方法谱聚类、普通图嵌入随机向量作为下限关键超参数walk_length40, num_walks10, window_size5, dim128先用 walk_length10 冒烟测试注意最后一行我的复现参数和论文不一样但必须在报告里写明为什么不一样。先用小图和短游走做冒烟测试是为了快速验证代码管道并不是最终参数。把这类决策写进报告老师就知道你清楚自己在做什么。如果论文正文没写超参数去附录找表格附录也没有就去作者开源的 README 或 issue 里翻。再不行就用一组常识性默认值并在报告里注明该项来自社区惯例论文未披露。这比编一个参数诚实得多。另一个容易忽略的点是数据集格式。如果论文原始数据需要特殊渠道获取可以换一个公开的等规模数据集但要说明差异。这一步叫界定实验边界它决定了后面所有代码的输入输出形态。2.2 把论文的算法流程画成数据流输入、采样、更新、评估DeepWalk 的整体流程是图 → 随机游走 → 节点序列 → Skip-gram → 节点向量 → 分类器。我习惯先用伪代码把数据流写出来再动手填充实现# DeepWalk 算法流程伪代码重点是输入输出边界 def deepwalk(graph, num_walks, walk_length, window_size, embed_dim): # graph: 邻接表例如 {0: [1, 2], 1: [0, 3]} # 1. 从每个节点出发做 num_walks 次随机游走 walks generate_random_walks(graph, num_walks, walk_length) # 2. 把游走序列交给 Skip-gram得到节点向量 embeddings skipgram(walks, window_size, embed_dim) # 3. 分类器吃向量输出节点标签预测 return embeddingsgenerate_random_walks 输出的是二维列表每一行是一条节点序列例如 [3, 7, 2, 5]。skipgram 内部用滑窗构造中心节点-上下文节点对再用 Word2Vec 训练。理解这个数据流之后复现的每一步都能确认这个模块的输入是什么输出给谁。参数说明walk_length 是游走长度太长会引入噪声太短表达不了局部结构num_walks 控制采样覆盖度window_size 影响上下文范围embed_dim 是最终向量的维度。这四个参数是 DeepWalk 复现时最常调的。把数据流画出来还有一个好处代码结构可以跟着它走。因为你知道数据从哪来、经过哪些环节、最后到哪去写代码时自然就能拆成多个模块而不是把所有逻辑塞进一个脚本。很多论文复现代码一打开就是几百个函数新手容易迷失我一般先按数据流拆把每个模块的输入输出写在 docstring 里即使某些细节看不懂也能通过边界判断它的作用。对一份机器学习项目的作业来说可读比巧妙重要得多。DeepWalk 的转移概率在论文里写得很简单从当前节点均匀随机地选择一个邻居作为下一步。实现时要注意如果节点有自环邻接表里出现自身游走可能会原地打转如果图是无向且带权转移概率要按权重归一化。复现时先按无权图实现后续再决定要不要支持权重。Skip-gram 的输入是节点序列但序列里可能重复出现同一个节点多次这是正常的。训练前需要把节点 id 映射到词表的整数索引否则 gensim 的 Word2Vec 会把它当成字符串。这一映射最好和 dataset.py 里的 id_map 保持一致否则就会遇到 5.4 节的错位问题。很多模型官方会放出训练好的向量那能不能直接拿来做分类实验可以但这不是论文复现而是模型使用。高分作业要求的是你亲自把论文的方法跑通一遍所以至少要把随机游走和 Skip-gram 训练这两段核心流程写在代码里即使最终结果略低于官方向量也说明你理解了方法。此外如果原论文提供了开源代码我的建议是不要先读训练部分先读数据加载和评估部分。因为这两部分是实验边界的直接体现。训练部分每个人写法不同但数据接口和评估逻辑必须和论文描述一致。表格里的 baseline 不要只填论文的自己加一个随机向量作为下限把节点 id 随机映射到等维度的向量用同样的分类流程评估。如果 DeepWalk 的指标还打不过随机向量说明实现有 bug。这个习惯能快速发现数据对不齐或标签泄漏的问题。3. 搭建可直接运行的复现工程目录、配置、数据加载论文复现代码能不能直接使用最直观的标准是别人把这个机器学习项目 clone 下来后根据 README 里的命令一路执行不需要额外改路径。我见过太多代码把自己电脑上的绝对路径写死在代码里换个环境就崩。这里的关键是目录结构清晰、配置集中、路径相对。3.1 目录结构设计让评分老师一眼看懂下面是一个 DeepWalk 复现作业的目录布局。它不是唯一答案但每个模块的职责非常清楚reproduce_deepwalk/ ├── README.md ├── requirements.txt ├── configs/ │ ├── deepwalk.yaml │ └── smoke.yaml ├── data/ │ ├── sample.edgelist │ └── sample.labels ├── src/ │ ├── __init__.py │ ├── dataset.py │ ├── random_walk.py │ ├── model.py │ ├── train.py │ └── evaluate.py └── scripts/ └── run_experiment.pysrc/dataset.py 负责读边列表和标签src/random_walk.py 负责生成游走序列src/model.py 封装 Skip-gram 嵌入src/train.py 组织训练流程src/evaluate.py 计算指标scripts/run_experiment.py 是唯一入口从配置读到训练评估串起来。README.md 里写清环境安装和复现命令这就是可直接使用的门面。逻辑说明这份结构把数据、配置、源码、入口分开。评分老师能顺着文件名找到每个环节而不是在一个巨型 main.py 里翻几百行。你写报告时也可以按数据 → 游走 → 嵌入 →分类的顺序对应到文件位置。需要遵守的约定是代码里只写相对路径基于项目根目录运行命令。不要做的事情不要把训练好的嵌入文件、日志文件、数据集压缩包堆在根目录里。它们应该在运行时生成放到 results/ 和 logs/ 下并在 .gitignore 里忽略。一份干净的项目树本身就是作业分数的一部分。3.2 用 YAML 集中管理超参数argparse 留给命令行覆盖作业场景下超参数散落在代码各处是大忌。常见做法是集中到一个配置文件里例如# configs/deepwalk.yaml data: graph_path: data/sample.edgelist labels_path: data/sample.labels model: embed_dim: 128 walk_length: 40 num_walks: 10 window_size: 5 train: workers: 4 epochs: 5 seed: 42 alpha: 0.025 evaluate: train_ratio: 0.9 metric: macro_f1加载配置时我用 PyYAML 的 safe_load再配合 argparse 做少量覆盖。比如临时想换一个随机种子不用改文件# scripts/run_experiment.py import argparse import yaml def load_config(): parser argparse.ArgumentParser() parser.add_argument(--config, defaultconfigs/deepwalk.yaml) parser.add_argument(--seed, typeint, defaultNone) args parser.parse_args() with open(args.config, r, encodingutf-8) as f: config yaml.safe_load(f) if args.seed is not None: config[train][seed] args.seed return config参数说明--config 指定配置文件路径--seed 覆盖配置里的随机种子。这样你在做参数敏感性实验时只改 YAML不动代码命令行覆盖只用于临时实验。yaml.safe_load 而不是 yaml.load避免反序列化任意对象的安全问题。为什么不用 argparse 写全部参数因为参数一多命令行长且容易抄错YAML 可以加注释记录每个参数的含义和论文里的取值。如果课程要求必须用命令行传参也可以保留 argparse但我更推荐配置文件 少量覆盖的组合这也是现在开源项目的主流。注意在代码里不要 import config 后直接读模块属性那会让路径纠缠不清YAML 才是项目的单一事实来源。3.3 图数据加载与预处理边列表到邻接表DeepWalk 的输入是一张图最常见的格式是边列表每行一条边两个节点 id。我写的加载函数长这样# src/dataset.py from collections import defaultdict def load_edgelist(path): graph defaultdict(list) id_map {} nodes set() with open(path, r, encodingutf-8) as f: for line in f: line line.strip() if not line or line.startswith(#): continue u, v line.split()[:2] if u not in id_map: id_map[u] len(id_map) if v not in id_map: id_map[v] len(id_map) uid, vid id_map[u], id_map[v] graph[uid].append(vid) graph[vid].append(uid) # 无向图 nodes.add(uid) nodes.add(vid) return graph, list(nodes), id_map逻辑说明先把字符串节点映射成连续的整数 id再用 defaultdict(list) 存邻接表。边列表是无向的所以同时写入两个方向。跳过空行和注释行这是数据文件里常见的脏数据。返回的 id_map 在后面映射标签时要用到。参数说明如果论文用的是有向图这里就别加 graph[vid].append(uid)同时在配置里声明 graph_type: directed。孤立节点不会进邻接表但会在 nodes 里做分类时要保留它否则评估索引对不上。这里踩过的坑我会在第 5 章详细展开。标签文件通常长这样两列第一列是节点第二列是类别。对单标签分类可以直接和 id_map 对齐# src/dataset.py def load_labels(path, id_map): labels {} with open(path, r, encodingutf-8) as f: for line in f: line line.strip() if not line: continue node_str, label line.split()[:2] if node_str in id_map: labels[id_map[node_str]] int(label) return labels注意如果标签文件里出现了 id_map 没有的节点直接忽略会损失数据。更稳妥的写法是先统计被忽略的节点数打印一条警告。这样在结果偏低时你能知道是数据对齐问题而不是算法问题。数据加载完我习惯马上做一次校验打印节点数、边数、标签数并检查是否存在度为 0 的节点# 脚本开头可选的 sanity check isolated [n for n in nodes if not graph[n]] print(fnodes{len(nodes)}, edges{sum(len(v) for v in graph.values())//2}, flabels{len(labels)}, isolated{len(isolated)})这段日志看起来不起眼但能在训练前暴露大部分数据问题。很多论文复现翻车都发生在这一步之前。4. 训练与评估让复现结果逼近论文的 3 个关键动作代码工程搭好训练是下一步。这里不讲怎么调神经网络只讲 3 个让结果可复现、可对齐的关键动作先跑通、锁随机源、对齐评估协议。这里讲的顺序很重要先冒烟再锁定随机源最后对齐评估。顺序反了你会在一个随机波动的指标上反复调参白白浪费一晚上。4.1 先跑通一个闭环用最小配置验证代码没写错我见过太多的复现代码在最终训练阶段报错等排查完已经耗尽信心。所以我在项目中加了一个 configs/smoke.yaml把参数全部缩到最小# configs/smoke.yaml data: graph_path: data/sample.edgelist labels_path: data/sample.labels model: embed_dim: 16 walk_length: 5 num_walks: 2 window_size: 2 train: workers: 2 epochs: 1 seed: 42 alpha: 0.025 evaluate: train_ratio: 0.9 metric: macro_f1运行命令python scripts/run_experiment.py --config configs/smoke.yaml冒烟测试只要观察三件事程序能完整跑完、训练 loss 在下降、评估函数输出一个正常的指标比如 macro-F1 在 0.1 到 1.0 之间。如果连这个都过不了就要把错误堆栈拉到最短先修数据流问题不要直接调大模型。参数说明embed_dim 从 128 降到 16walk_length 从 40 降到 5num_walks 从 10 降到 2epochs 从 5 降到 1训练时间从分钟级降到秒级。注意冒烟测试的结果不能写进报告它的作用只是验证代码管道没有断。等闭环保通后再恢复论文参数跑正式实验。冒烟测试通过后再用 deepwalk.yaml 跑正式训练。此时最好开启日志记录包括每个 epoch 的 loss 和当前时间方便判断训练是否收敛。如果 loss 不降先看数据是否对齐再调 alpha。不要把训练日志只打在屏幕输出里写进 logs/train.log 更干净。4.2 随机种子与多进程复现的确定性来自哪里论文复现中最影响指标稳定性的不是算法本身而是随机性。随机游走、负采样、逻辑回归初始化、数据划分都可能引入随机数。如果不设置种子两次运行结果可能差 2 到 3 个百分点你就没法判断改动是有效还是噪声。# src/train.py import os import random import numpy as np def set_seed(seed): os.environ[PYTHONHASHSEED] str(seed) random.seed(seed) np.random.seed(seed)逻辑说明PYTHONHASHSEED 影响字符串哈希在数据加载时如果依赖节点 id 的哈希顺序会引入随机性。random.seed 和 np.random.seed 覆盖 Python 和 NumPy 的随机源。注意这个设置要在任何数据加载之前调用顺序不对就锁不住。如果你用了 PyTorch还需要 torch.manual_seed(seed)用了 gensim 的 Word2Vec则需要给 Word2Vec 传 random_state。全局种子管不到这些框架内部的随机源。这个细节是很多人复现结果对不上的原因。另外gensim 的 Word2Vec 默认使用多线程workers 参数会影响数据打乱顺序。单线程和多线程训练得到的嵌入不是完全一致。想完全复现就把 workers 固定在配置里比如 4不要在不同机器上换来换去。4.3 评估协议对齐为什么你的数字和论文差一截这是复现翻车的高发区。DeepWalk 的节点分类评估通常是用部分节点比如 10% 训练逻辑回归在剩余节点上测试多次随机划分取平均。但论文可能用了某个特定的 train/test split你没注意导致结果偏差。我的评估代码会显式控制划分比例并固定种子# src/evaluate.py from sklearn.model_selection import train_test_split from sklearn.linear_model import LogisticRegression from sklearn.metrics import f1_score def evaluate_embeddings(embeddings, labels, train_ratio0.9, seed42, metricmacro_f1): X [embeddings[n] for n in sorted(labels.keys())] y [labels[n] for n in sorted(labels.keys())] X_train, X_test, y_train, y_test train_test_split( X, y, train_sizetrain_ratio, random_stateseed, stratifyy ) clf LogisticRegression(max_iter1000) clf.fit(X_train, y_train) y_pred clf.predict(X_test) if metric macro_f1: return f1_score(y_test, y_pred, averagemacro) return f1_score(y_test, y_pred, averagemicro)逻辑说明先按节点 id 排序确保特征和标签对齐再用 train_test_split 按比例划分。stratifyy 让训练集和测试集的标签分布保持近似这对不平衡标签很重要。random_stateseed 保证每次划分一致。参数说明train_ratio0.9 表示用 90% 节点训练10% 测试。很多论文写的是用 10% 的节点训练那要设置 train_size0.1 还是 train_sizetrain_ratio一定要看论文的表述。我曾经在这里把 ratio 和 size 搞反结果低了 10 多个点。还有如果标签是多标签分类stratify 不能直接用需要换成 multi-label 的评估方式或者评估每个标签的 F1 再取平均。如果论文用了不同的分类器比如 SVM 或 MLP你要注意DeepWalk 原论文用逻辑回归作为下游分类器有些复现会用别的模型。不同分类器对嵌入质量的评价标准不同换分类器就等于改变实验设置。所以第一版复现务必用逻辑回归等结果对齐后再考虑其他分类器。在训练中如何判断收敛除了看最终指标我还会记录每个 epoch 的 loss。Skip-gram 的 loss 通常不会降到 0而是在一个区间内波动如果 loss 一直不降问题多半在前面的数据加载。如果只打印最终结果中间过程就是黑匣子出了问题很难定位。有一次我复现一篇交通类论文的样例代码时数据加载和模型都写好了但训练一直不收敛最后发现是数据归一化顺序写错。从那以后我固定用一个 sanity check 函数在训练前打印输入数据形状、均值和方差如果均值不是有限数就直接终止。更稳妥的做法是所有评估都保留打印 macro_f1 和 micro_f1 两个值在报告里同时给出print(fmacro_f1{macro_f1:.4f}, micro_f1{micro_f1:.4f})跑 5 次取平均时最好把 5 次结果都写进 results/metrics.json而不是只写均值。这样老师能直接看到方差比一句稳定更有说服力。我一般保存成 metrics_seed42.json、metrics_seed123.json 这样的文件。5. 论文复现代码避坑指南5 个常见的翻车现场前面几章的流程都走过之后剩下的就是查漏补缺。下面 5 个坑是我在复现 DeepWalk 类似项目时反复看到的也在自己的作业里踩过。每条按现象 → 原因 → 解决写希望能让你少走一点弯路。5.1 依赖版本不一致装了 3 小时最后发现是 gensim 版本现象随机游走代码和网上的教程一致但训练 Word2Vec 时报AttributeError: Word2Vec object has no attribute wv或者跑完结果和别人的完全不一样。原因gensim 4.x 改了接口model[node]必须写成model.wv[node]。如果代码是从旧教程抄的就会踩中。sklearn、NumPy 版本不同逻辑回归的收敛行为也会略有差异导致同样的代码在不同环境下跑出不同指标。解决把版本锁在 requirements.txt 里写明gensim4.3.2、scikit-learn1.3.0、numpy2.0。如果一定要用旧接口就装gensim3.8.3并调整代码。我一般用pip freeze requirements.txt生成初始文件再人工清理无关项。复现代码要能在干净环境里一键安装而不是靠你电脑里的历史包。5.2 数据划分不一致你的训练集和论文的训练集不是一回事现象跑出来的 macro-F1 比论文低 5 个点但算法实现看起来没问题。原因你用了 train_test_split 默认的 75% 训练而论文用 10% 或 50% 训练或者论文用了固定划分文件你每次重新随机划分相当于换了一个数据集。同一个模型在不同数据划分上的表现差异很容易超过 5 个点。解决回到 2.1 的实验边界表确认论文的 train/test split 方式和比例。如果论文没有明确就按随机划分 5 次平均写在报告里并在 README 中说明。最稳妥的是用和论文一致的划分文件找不到时固定种子并把划分逻辑写到配置里让别人能复现你的划分。5.3 指标口径错误macro-F1 还是 micro-F1现象你的 micro-F1 很高但报告里抄成 macro-F1看起来和论文差一截或者反过来你报了 macro-F1但论文用的是 micro-F1。原因多标签分类里 macro-F1 先按类别算 F1 再平均对少数类敏感micro-F1 把所有类别的 TP、FP、FN 汇总再算指标值通常高于 macro。两个口径混用是复现报告里最常见的低级错误。解决在配置文件里明确 metric: macro_f1代码里写死 metric 名评估时同时输出两个值。报告里说清你报的是哪个最好两个都给。最后再对照论文实验表确认论文用的是哪个口径别闭着眼抄数字。5.4 随机游走漏了孤立节点或标签错位现象训练不报错但画嵌入图时一堆节点堆在原点上或者分类时索引越界。原因随机游走时如果节点度为 0就没有邻居可走循环会卡住或返回空序列。另一个常见问题是节点 id 映射对不上边列表用的 id_map 和标签文件用的 id_map 不是同一份导致 labels 错位。解决加载邻接表后立刻打印孤立节点数量游走函数对孤立节点返回空序列加载标签时用边列表生成的 id_map 做转换并把没匹配上的标签打印出来。我习惯在数据预处理后输出日志# 诊断代码片段 isolated [n for n in nodes if not graph[n]] print(fnodes{len(nodes)}, isolated_nodes{len(isolated)}, labels{len(labels)})这行日志能让你在训练前就发现数据问题而不是等 20 分钟训练完再面对一个烂结果。调代码时看起来很慢实际上比反复训练快得多。5.5 训练不稳定loss 变成 NaN 或嵌入向量发散现象训练到某一步打印 loss 为 nan或者嵌入向量的模越来越大分类结果断崖式下降。原因常见于学习率过高或负采样没做归一化。在 Skip-gram 里如果上下文窗口出现重复节点更新步长过大梯度会溢出。gensim 的 alpha0.025 是经验值但如果你把 min_count 设成 0或窗口里全是低频节点也可能触发。解决先用默认超参数跑通再尝试调参。如果 loss 震荡把 alpha 调小一个数量级例如从 0.025 降到 0.0025。设置 min_count1 可以保留所有节点但要注意低频节点的统计噪声。还有一种做法是给梯度加裁剪但 gensim 不直接暴露这个接口所以降低学习率是最实际的手段。记录每个 epoch 的 loss方便定位是第几步开始发散的。遇到上述任何问题我的第一反应不是搜索代码而是先构造一个最小复现把数据集换成一棵只有 3 个节点的树把 walk_length 换成 2看问题还在不在。这个习惯能帮你把环境问题和算法问题快速分开。很多时候问题不在训练脚本而在数据加载和版本依赖上。能在出错信息里看到数据的形状比看到模型结构更有用。6. 把复现代码变成高分作业一键复现脚本与验证清单前面几章的内容做完代码已经能跑但离高分作业还差一步让别人尤其是评分老师能一条命令从头复现你的全部结果。这一章讲怎么把实验入口收拢以及交付前怎么验证。完整入口脚本应该把配置加载、数据读取、游走、训练、评估、结果持久化全部串起来# scripts/run_experiment.py 完整入口骨架 import json from src.dataset import load_edgelist, load_labels from src.random_walk import generate_random_walks from src.model import train_skipgram from src.evaluate import evaluate_embeddings def main(): config load_config() # 见 3.2 节 graph, nodes, id_map load_edgelist(config[data][graph_path]) labels load_labels(config[data][labels_path], id_map) walks generate_random_walks(graph, config[model][num_walks], config[model][walk_length]) embeddings train_skipgram(walks, config[model][embed_dim], config[model][window_size]) macro_f1 evaluate_embeddings( embeddings, labels, config[evaluate][train_ratio], config[train][seed], config[evaluate][metric] ) result {macro_f1: macro_f1, config: config} with open(results/metrics.json, w, encodingutf-8) as f: json.dump(result, f, indent2, ensure_asciiFalse) print(json.dumps({macro_f1: macro_f1}, indent2)) if __name__ __main__: main()逻辑说明results/metrics.json 里同时保存指标和完整配置这样后来任何时候都能知道该指标是在什么参数下跑出来的。注意脚本运行之前要确保 results/ 目录存在否则用 os.makedirs(results, exist_okTrue) 包一下。交付前我会按这张清单过一遍检查项具体标准环境可复现requirements.txt 锁定版本README 有一键安装命令路径可移植代码中无绝对路径全部相对项目根目录数据可追溯边列表、标签与 id_map 对齐孤立节点已统计训练可复现随机种子固定训练日志有记录loss 收敛评估可对齐train_ratio 和 metric 与论文一致跑 5 次取均值一键可复现运行 run_experiment.py 后能从零产出 results/metrics.json最后给你一个加分技巧做参数敏感性实验。把 walk_length 从 10 改成 20、40记录 macro-F1 的变化画一张小表写进报告。如果代码里支持通用覆盖参数比如 --set model.walk_length 40就可以用一个循环批量跑for wl in 10 20 40; do python scripts/run_experiment.py \ --config configs/deepwalk.yaml \ --set model.walk_length $wl done这组实验能让老师看到你不是只会跑通代码而是真的理解每个参数对结果的影响。我自己以前交作业只交 main.py老师问了三个问题全答不上来后来花一小时补上配置、README、固定种子和评估脚本结果从良变成了优。高分作业不是算法最炫的而是最容易验证的。代码能跑只是起点可复现才是终点。希望帮到你。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。