简介:SCMA-ML-master 是一份面向无线通信与机器学习交叉方向研究者的开源代码资源,聚焦 SCMA(稀疏码多址)系统的算法实现与性能优化。SCMA 作为码域非正交多用户接入技术,可提升频谱效率与抗干扰能力,而该资源尝试引入机器学习方法辅助信号检测、编解码与参数调优,适合具备通信原理与 Python 编程基础的学习者研读。压缩包共 9 个文件,以 m 文件为主,辅以 license、s 与 md 文档,整体约 6KB,体量轻巧但结构完整,涵盖码本设计、SCMA 编解码、ML 检测及四进制与二进制转换等核心模块,README 与许可证文件便于快速了解项目背景与使用约束。目前已有 227 人学习下载,读者可借此理解 SCMA 与机器学习结合的基本流程,包括数据预处理、模型训练、误码率等指标评估,并参考其目录组织方式搭建自己的仿真实验框架,对通信工程与机器学习方向的研究具有较高参考价值。
1. SCMA-ML-master 到底在做什么:从稀疏码多址到机器学习接收机
如果你手里只有一份名为SCMA-ML-master的代码包,打开后看到morehsq、SCMA、ML这些目录名,第一反应大概率是:这是一个把机器学习塞进稀疏码多址(Sparse Code Multiple Access, SCMA)系统里的仿真工程。SCMA 本身是 5G 非正交多址接入(NOMA)家族里被讨论最多的一种方案,核心思路是让多个用户在相同的时频资源上叠加发送,靠码本的稀疏性在接收端做多用户检测。而ML通常指 Maximum Likelihood 检测,也可能是 Machine Learning 的缩写——在这个项目名里,两者其实都说得通,因为 SCMA 的接收机设计正好处在传统最大似然检测和深度学习检测的交汇点上。
这个方向适合谁?如果你在做通信物理层仿真、想复现 SCMA 的码本设计和消息传递算法(MPA),或者想对比 ML 检测和深度学习方法在 SCMA 上的误码率表现,这份代码包就是一个能跑的起点。它解决的核心问题是:在用户数超过资源数、码本又稀疏的情况下,接收端怎么把叠加的信号拆开。传统 MPA 复杂度随用户数指数增长,ML 检测虽然最优但穷举量太大,而机器学习方法试图在两者之间找平衡。接下来我会按「先跑通、再调参、最后避坑」的顺序,把这条路径拆开讲清楚。
2. 把 SCMA-ML-master 在本地跑起来:环境、入口与最小验证
2.1 先看清目录结构再动手
拿到一个通信仿真包,最忌讳的就是直接python main.py。SCMA-ML-master 这类工程通常包含码本生成、信道建模、检测算法、误码率统计几个模块。我一般会先花五分钟把目录树扫一遍,确认入口脚本、配置文件和数据输出路径。常见结构是SCMA/放码本和因子图,ML/放检测器实现,morehsq/可能是作者自己的实验脚本或参数扫描目录。如果目录名带master,说明这是主分支快照,不一定有完整的依赖声明文件。
# 先看目录层级,不要急着运行 find . -maxdepth 2 -type d | sort # 再看有没有 requirements.txt 或 environment.yml ls -la | grep -E "requirements|environment|setup" # 确认 Python 版本和关键依赖 python --version pip list | grep -E "numpy|scipy|matplotlib|torch|tensorflow"上面这段命令的逻辑是:先摸清目录边界,再确认依赖是否齐全。参数上,-maxdepth 2避免输出太深,grep -E用来快速过滤关键词。如果发现没有依赖文件,就需要根据 import 语句反推。常见依赖是numpy做矩阵运算、scipy做特殊函数、matplotlib画 BER 曲线,如果ML指深度学习,还会出现torch或tensorflow。
2.2 最小可运行入口与参数含义
跑通的第一步不是复现论文,而是让程序输出一个非零的 BER 结果。我通常会在入口脚本里找if __name__ == '__main__'或者config字典,把用户数、资源数、码本大小、信噪比范围这几个参数先固定成小值。
# 典型的 SCMA 仿真参数配置片段 config = { "K": 4, # 资源元素数,即 RE 数量 "J": 6, # 用户数,J > K 体现过载 "M": 4, # 每个用户的码本码字数量 "N": 2, # 每个码字占用的非零资源数,稀疏度 = N/K "snr_db": list(range(0, 16, 2)), # 信噪比扫描范围 "channel": "rayleigh", # 信道类型 "detector": "ml" # 检测器选择:ml / mpa / dnn }这段配置里,K=4, J=6表示过载率 150%,这是 SCMA 的典型场景。N=2决定码本稀疏度,N 越小检测越容易但性能会降。snr_db从 0 到 14 步进 2,是为了画 BER 曲线。detector字段是切换算法的关键,如果代码里同时实现了 ML 和 MPA,改这个值就能对比。运行后先看输出目录有没有ber.npy或ber.csv,如果有,说明主流程通了。
2.3 验证码本和因子图是否对得上
SCMA 的码本不是随便生成的,它和因子图矩阵一一对应。因子图矩阵F是 K 行 J 列,F(k,j)=1表示第 j 个用户在第 k 个资源上发送非零符号。如果代码包里码本维度和因子图不匹配,后面检测全是错的。
import numpy as np # 假设从文件加载因子图矩阵 F = np.load("SCMA/factor_graph.npy") print("因子图维度:", F.shape) # 应为 (K, J) print("每列非零数:", np.sum(F, axis=0)) # 应等于 N print("每行非零数:", np.sum(F, axis=1)) # 通常大于 1 # 检查码本维度:每个用户一个码本,形状 (M, N) codebook = np.load("SCMA/codebook.npy") print("码本维度:", codebook.shape) # 应为 (J, M, N)这里的关键是确认F的每列非零数等于N,每行非零数大于 1。如果对不上,要么是加载错了文件,要么是码本生成脚本没跑完。我见过有人直接拿论文里的因子图矩阵硬编码,结果和代码里的码本维度差一位,BER 曲线直接变成一条水平线,这就是典型的“翻车”现场。
3. ML 检测在 SCMA 里怎么落地:从穷举到可跑通的实现
3.1 最大似然检测的数学形式与复杂度边界
SCMA 的接收信号可以写成y = Hx + n,其中x是所有用户码字的叠加。ML 检测就是在所有可能的用户码字组合里找一组,使||y - Hx||^2最小。对于J个用户、每个用户M个码字,穷举量是M^J。当J=6, M=4时,4^6=4096种组合,还能接受;但如果J=12, M=4,就是 1600 万种,单机跑一次 BER 点要很久。所以 ML 检测在 SCMA 里的定位是“小规模最优基准”,用来验证 MPA 或神经网络检测器是否接近最优。
def ml_detect(y, H, codebooks): """ y: 接收信号,形状 (K,) H: 信道矩阵,形状 (K, J) codebooks: 所有用户码本,形状 (J, M, N) 返回: 最优用户码字组合索引 """ J, M, N = codebooks.shape K = y.shape[0] best_idx = None min_dist = np.inf # 穷举所有组合 for combo in np.ndindex(*([M] * J)): x = np.zeros(K, dtype=complex) for j in range(J): # 把第 j 个用户的第 combo[j] 个码字映射到非零资源上 x += map_to_resources(codebooks[j, combo[j]], j) dist = np.linalg.norm(y - H @ x) ** 2 if dist < min_dist: min_dist = dist best_idx = combo return best_idx这段代码的逻辑很直白:用np.ndindex生成所有码字组合,逐个计算欧氏距离。参数上,codebooks的维度必须是(J, M, N),map_to_resources负责把N维码字放到K维资源上。实际跑的时候,如果J超过 8,建议先加剪枝或者改用 MPA,否则一个 SNR 点就能跑十几分钟。
3.2 用向量化把 ML 检测速度提上来
纯 Python 循环穷举在J=6时还能忍,但做参数扫描就太慢。我一般会用 NumPy 做向量化,把所有候选组合预先展开成矩阵,一次性算距离。
def ml_detect_vectorized(y, H, codebooks): J, M, N = codebooks.shape K = y.shape[0] # 生成所有组合的索引,形状 (M^J, J) combos = np.array(np.unravel_index( np.arange(M**J), [M]*J)).T # 构造所有候选叠加信号,形状 (M^J, K) X_candidates = np.zeros((M**J, K), dtype=complex) for j in range(J): for n_idx in range(N): # 找到第 j 个用户第 n_idx 个非零资源的位置 res_idx = np.where(factor_graph[:, j] == 1)[0][n_idx] X_candidates[:, res_idx] += codebooks[j, combos[:, j], n_idx] # 向量化计算距离 diff = y[None, :] - X_candidates @ H.T dists = np.sum(np.abs(diff)**2, axis=1) return combos[np.argmin(dists)]向量化之后,M^J个候选一次性算完,速度能快一个数量级。注意factor_graph的列索引要和码本的非零位置对应,否则叠加信号会错位。这个函数在J=6, M=4时大概几毫秒出结果,适合做 BER 扫描。
3.3 和 MPA 检测的对比:什么时候该用 ML
ML 检测是性能上界,但复杂度也最高。实际做对比实验时,我一般会固定K=4, J=6, M=4,分别跑 ML、MPA 和 DNN 三种检测器,看 BER 曲线在哪个 SNR 点分叉。MPA 的迭代次数通常设 5 到 10 次,次数越多越接近 ML,但时延也越大。如果代码包里morehsq目录有现成的对比脚本,直接改detector字段就行;如果没有,就自己写一个循环,把三种检测器的 BER 存到同一个.npz里,再用matplotlib画图。这一步的坑在于:不同检测器的输出格式可能不一样,有的返回比特,有的返回符号索引,对齐的时候要特别小心。
4. 参数怎么调、结果怎么看:SCMA-ML 仿真的调参与验证
4.1 过载率和稀疏度的取舍
SCMA 的核心卖点是过载,J/K越大,接入用户越多,但检测难度也越大。常见配置是K=4, J=6(过载 150%)或K=4, J=8(过载 200%)。N一般取 2,因为N=1就退化成正交多址,N=3又太稠密,MPA 的因子图会变复杂。调参的时候,我习惯先固定N=2,然后逐步增加J,看 BER 在什么用户数下开始急剧恶化。这个拐点就是这套码本和检测器的实际容量边界。
| 参数 | 典型值 | 影响 |
|---|---|---|
| K | 4 | 资源数,越小过载越高 |
| J | 6 | 用户数,越大检测越难 |
| M | 4 | 码本大小,越大频谱效率越高 |
| N | 2 | 稀疏度,越小检测越简单 |
| 迭代次数 | 5~10 | MPA 专用,越大越接近 ML |
4.2 BER 曲线的正确读法
BER 曲线不是越陡越好,要看在目标误码率(比如1e-3)下的 SNR 门限。ML 检测的曲线通常最靠左,MPA 会右移 1 到 2 dB,DNN 检测器如果训练充分,可以逼近 ML。但如果 DNN 曲线在低 SNR 反而比 MPA 差,说明训练集覆盖不够或者网络过拟合。我一般会跑snr_db = 0:2:14,每个点至少1e5个比特,否则高 SNR 段的 BER 抖动很大,看起来像“玄学”。
# 简单的 BER 统计循环 ber = [] for snr in config["snr_db"]: errors = 0 total = 0 for _ in range(1000): # 每个 SNR 跑 1000 帧 y, bits_true = simulate_frame(snr, config) bits_est = detector(y, config) errors += np.sum(bits_true != bits_est) total += len(bits_true) ber.append(errors / total)这段代码里,simulate_frame负责生成信道和叠加信号,detector是前面实现的 ML 或 MPA。1000帧是经验值,如果 BER 低于1e-4,需要加到1e5帧才能看到稳定结果。
4.3 用星座图辅助排查映射错误
如果 BER 曲线明显偏离预期,比如 ML 检测和 MPA 几乎重合,那大概率是码本映射或者信道模型出了问题。我一般会画接收信号的星座图,看叠加后的符号是否落在预期的码字组合上。如果星座点散乱,说明H矩阵或者码本归一化有问题。这一步不需要改代码,用matplotlib的scatter就能看出来。
5. 避坑与排查:SCMA-ML 仿真里最容易翻车的 4 个点
5.1 现象:BER 曲线是一条水平线
原因:检测器输出和真实比特没有对齐,或者码本维度加载错误。常见于codebook.npy的(J, M, N)被误读成(M, J, N)。解决:打印codebook.shape和factor_graph.shape,确认J和K的对应关系。再用一个已知码字手动叠加,看ml_detect能否返回正确索引。
5.2 现象:ML 检测跑得极慢,一个 SNR 点要半小时
原因:用了纯 Python 循环穷举,没有向量化,或者J设得太大。解决:换成ml_detect_vectorized,或者先把J降到 4 验证流程。如果必须跑J=8以上,考虑用 MPA 做基准,ML 只跑一个 SNR 点做上界参考。
5.3 现象:DNN 检测器训练 loss 不下降
原因:输入特征没有归一化,或者训练集和测试集的信道模型不一致。解决:把接收信号y按功率归一化,标签用 one-hot 码字索引。检查morehsq目录里的数据生成脚本,确保训练和测试用的是同一套H分布。
5.4 现象:MPA 迭代次数增加但 BER 不变
原因:因子图存在环,消息传递不收敛,或者阻尼因子设得不对。解决:检查因子图矩阵是否有短环,适当增加阻尼因子(比如 0.5 到 0.7)。如果还是不降,说明码本设计本身有问题,换一组论文里的标准码本再试。
6. 进阶技巧:把 SCMA-ML 的检测器换成可训练模块
如果你已经跑通了 ML 和 MPA,下一步大概率是想试试深度学习检测器。我的习惯是不动原有仿真框架,只在检测器接口上做替换。具体做法是:把ml_detect的输入输出固定成(y, H) -> 码字索引,然后写一个 PyTorch 模块,用全连接网络或者 CNN 拟合这个映射。训练数据就用仿真循环生成,标签是真实码字组合。关键技巧是:网络最后一层用softmax输出M^J维概率,但M^J可能很大,所以实际会用因子图结构做分组检测,把M^J拆成J个M分类问题。验证的时候,先看训练集准确率能不能到 95% 以上,再看 BER 曲线是否逼近 ML。如果 DNN 在低 SNR 比 MPA 还差,别急着调网络,先检查训练集里低 SNR 样本是不是太少。这个方向我踩过最大的坑是:仿真生成的H矩阵在训练和测试时用了不同的随机种子,导致网络学到的映射完全对不上,BER 直接爆表。后来养成习惯,所有随机种子固定,数据生成和检测器评估分开跑,才稳定下来。希望帮到你。
本文还有配套的精品资源,点击获取