轻量级训练监控库:多 GPU 指标合并 + 控制台实时进度条 + Web 可视化。
epoch 3 loss=0.2153 acc=0.8974 lr=8.2e-04 ━━━━━━━━━━━────────── 45.0% [90/200] [0:03<0:04 30.0it/s]
特性
- 控制台实时进度条:自研 tqdm(用法与 tqdm.tqdm 一致),
[n/total]领先、指标紧随其后实时刷新;终端下自适应列宽渲染——内容不超行、绝不自动换行:进度条吃掉固定段之外的全部剩余列恰好占满整行,剩余不足时收缩到最小宽度,仍放不下的指标以省略号收尾(重定向 / 日志文件无列宽概念,按固定宽度渲染);进度条与 print 同步镜像到每次会话独立的console-<时间戳>.log(run 目录已有会话产物时新文件自动加序号前缀2.、3.……,metrics 日志同理;进度条行就地实时刷新、带时间戳前缀,与终端内容一致,编辑器打开可看到动的进度条) - 多 GPU 指标合并:基于
torch.distributedall_reduce 跨进程聚合(默认取均值),仅 rank0 记录与展示;未装 torch 自动退化单进程。StepsBar(reduce=False)可关闭合并(训练期间只看 master 实时值,零集合通信) - Web 可视化:FastAPI + WebSocket + ECharts,后台线程运行,实时推送曲线,断线自动重连;支持
tab分区(train / val / test 垂直分块,各分区 step 独立计数)与层级命名多系列卡片(多分类逐类曲线一图对比) - 持久化:指标写入自研二进制容器(符号表 + varint 增量编码,体积约为 JSONL 的 1/4),每次启动一个带时间戳的会话文件,互不覆盖
- 断点续训:重跑同一
log_dir自动接续历史曲线;从某个 epoch 重新训练时自动清除上次中断留下的重叠数据,折线不会在 x 轴上回退
安装
pip install melog # 基础安装,来自 PyPI
多 GPU 合并基于 torch.distributed,假定环境中已装好 PyTorch;未装 torch 时自动退化单进程。
快速开始
import melog
from melog import StepsBar
melog.init("runs/my-exp") # 日志保存路径;端口缺省自动选空闲端口
# Web 地址启动时自动打印,也可读 melog.current().web_url
for step in StepsBar(range(1000)): # tqdm 风格:自动推进,无需手动 update
loss = train_one_step()
melog.scalar({"loss": loss, "lr": 1e-3}) # 记录 + 刷新进度条指标 + 推送 Web
训练期间浏览器打开启动时打印的 Web 地址(即 melog.current().web_url)查看实时曲线。
全局共享
melog.init 是唯一入口,创建的实例自动成为全局活动实例。入口处 init 一次,
项目任何地方直接用模块级接口,无需层层传递实例:
import melog
melog.init(log_dir="runs/my-exp") # 日志保存路径;端口缺省自动选空闲端口
# 任意其他模块中:
import melog
melog.scalar({"loss": 0.5})
melog.image("sample", img)
# 收尾:进程退出时自动完成,无需调用
log_dir末级目录名即项目名(runs/my-exp→ 项目my-exp),本次运行落在runs/my-exp/<时间戳>/下;project=可覆盖项目名- 最近一次创建的实例即全局活动实例(
melog.current()取回),收尾后清空 - 进程退出时经 atexit 自动收尾:落盘剩余指标、定稿进度条、停 Web、还原 print, 无需任何手动调用
- 模块级
scalar / image / audio / log / success / error / warn / set_colors / current_bar与实例方法等价 - 实例内部有锁,多线程 / 多模块共享安全;多 GPU 约定不变
曲线上体现 epoch
本库按 epoch 组织训练记录:每个 epoch 的循环必须用 StepsBar 包裹并传入
epoch,坐标(epoch / step)由它统一管理——scalar() / image() /
audio() 都没有坐标参数,记录自动依附当前 epoch 与下一个空槽:
from melog import StepsBar
for epoch in range(epochs):
for _ in StepsBar(loader, epoch=epoch): # 行首自动标注 "epoch N"
loss = train_one_step()
melog.scalar({"loss": loss, "lr": lr}) # 坐标自动依附当前 epoch
StepsBar(epoch=...)进入进度条即绑定 epoch:epoch 内步数清零、全局 x 从上一位置 接续;bar 结束后沿用绑定值,直至下一个 epochstep为当前 epoch 内的记录序号,内部自增(每个 epoch 从 0 重新计步); 完全没用StepsBar(epoch=...)时退化为全局自增 x、不标注 epoch 分界- 要控制记录粒度(每步 / 每 N 步窗口),调整调用
scalar()的频率即可,无需手动指定坐标 - Web 曲线在每个 epoch 起点画分界虚线(标注
e0/e1/ …),悬浮提示显示epoch=N · step X MetricGroup末尾收尾交给StepsBar的自动记录(见下文),epoch 沿用绑定值
控制台消息
print 风格的控制台输出接口:多参数自动转 str()、以 sep 拼接,签名对齐 print
(支持 sep / end / flush):
melog.log("普通消息", {"k": 1}) # 终端默认色(黑字),无前缀
melog.success("保存完成") # 绿色 ✔
melog.error("加载失败") # 红色 ✘
melog.warn("学习率过大") # 黄色 ⚠
实例存活期间(仅 rank0),官方 print(...) 会被拦截内部改走 log()——普通打印
自动带上图标/配色并同步进 console.log,进程退出收尾后还原原生 print。颜色仅在真实
终端(TTY)启用,重定向 / console.log 始终纯文本。
多 GPU 下日志消息默认仅 rank0 输出(与指标记录一致,避免 N 卡重复刷屏);
调试需要各卡都打印时传 all_ranks=True(非主卡仅上终端,不进 console.log——
镜像只在 rank0 挂载):
melog.log("只有 rank0 打印") # 默认:其余卡静默
melog.log("每个卡都打印自己的 local 值", all_ranks=True) # 调试用
记录图像与音频
除指标曲线外,Web 端 header 可在 曲线 / 图像 / 音频 三个页签间切换。图像与音频用
image / audio 记录,Web 端按名字建卡片、滑杆按 step 回放(图像点击看原图,
音频在线播放);文件自动落盘到 run_dir/media/,元数据随日志持久化,历史日志加载时
媒体一并恢复:
melog.scalar({"loss": loss}) # 坐标自动依附当前 epoch(StepsBar 绑定)
melog.image("train/sample", img) # 路径 / PIL / numpy / torch
melog.image("val/sample", img) # 自动附着最近一次 scalar() 的位置
melog.audio("val/audio", wav, sr=16000) # 路径(wav/mp3/…) / numpy / torch 波形
- 图像 / 音频自动附着到最近一次
scalar()的位置,不推进计数; 坐标(epoch / step)由StepsBar统一管理,接口无坐标参数 caption="..."可为每条图像 / 音频配一段文字(如样本说明、转写文本), 显示在卡片上、随滑杆切换;换行会被保留- 图像:
(H,W)灰度或(H,W,C)(C=1/3/4),浮点自动映射 0-255,统一存为 PNG - 音频:
(N,)单声道或(N, 声道数),浮点按 [-1,1] 裁剪存为 16bit WAV; 传文件路径则按原格式复制 - 数组编码需要
pillow(仅图像):pip install pillow
指标计算(多 GPU 自动同步)
内置 Mean / Sum / Last / Count,按 epoch 组织在 MetricGroup 中使用:
import melog
from melog import Last, Mean, MetricGroup, StepsBar, Sum
melog.init("runs/my-exp")
metrics = MetricGroup({
"loss": Mean(), # 各 batch 等权平均
"acc": Mean(),
"seen": Sum(), # 求和
"lr": Last(), # 最近一次喂入值
})
for epoch in range(epochs):
# metrics=... 传入后:每次 feed 自动把本卡本地值写入日志/面板
# (实时曲线,零通信),epoch 末自动跨 GPU 合并出全局值再记录
# 一次并 reset 清零。训练曲线(loss / recall 等)无需跨卡合并时传
# reduce=False:bar 打开期间的所有记录都跳过 all_reduce,只记
# master 实时值(各 rank 无需在对齐位置调用);epoch 末只 reset
for _ in StepsBar(range(steps), epoch=epoch, metrics=metrics, reduce=False):
metrics.feed(loss=loss, acc=acc, seen=batch_size, lr=lr)
# feed(..., write=False) 只累积内存(如验证集不想逐 batch 写曲线);
# epoch 末仍自动跨 GPU 合并记录 + reset,无需手动 scalar(默认
# reduce=True,对验证集测试集结果多卡合并)
-
Mean默认按 StepsBar 自动识别的批次样本数精确平均(feed 无需传 batch_size;各 batch 等样本数时即等权);多 GPU 下合并为全局样本平均, 而非"各卡平均值的平均" -
批次样本数自动识别(tensor / numpy 的 shape[0]、字典、列表 / 元组递归); 识别失败(如迭代 range)回退等权平均并警告一次。需手动指定时传元组
(值, 观测数):metrics.feed(loss=(loss, token_num)),显式值优先 -
melog.scalar(metrics)随时可落盘当前累计值;必须算完一个 epoch 才有意义的指标, 在 epoch 末统一记录一次即可(交给 StepsBar 自动执行,或手动调用) -
跨 GPU 合并是集合操作:所有 rank 必须以相同顺序执行,返回值各 rank 一致;单进程自动直通
-
实时 + 精确一步到位:
StepsBar(loader, epoch=e, metrics=metrics)—— 每次 feed 自动把本卡本地值写入日志/面板(零通信,仅 rank0 落盘),bar 同步实时显示; 迭代自然结束时自动 gather 所有 rank 合并出全局值再记录一次(提前 break / 抛异常不触发, 以免各 rank 在 all_gather 处互相等待;所有 rank 都会执行,落盘仅 rank0)。feed(..., write=False)关闭逐 batch 实时写入(如验证集场景),epoch 末 仍自动合并记录,无需手动 scalar -
bar 跑完自动打印最终结果:
StepsBar(metrics=...)迭代自然结束时把 本 bar 监控的 metrics 最终结果打印到控制台(bar 行上方一行)——reduce=True打印跨卡合并后的结果(与落盘值一致),reduce=False打印本卡本地累计值(重置前);未观测到的指标(NaN)与非数值结果不 打印,仅 rank0 输出;print_result=False关闭 -
on_end=...:epoch 末自动记录完成后触发的回调,参数为跨 GPU 合并后的 指标字典(与落盘值一致,未观测到的指标为 NaN)。典型用途如按验证指标 保存 checkpoint。需配合metrics使用且reduce=True(reduce=False 时 epoch 末不做合并记录、本回调不触发,同时传入会被拒绝);所有 rank 都会 执行(合并是集合操作),各卡收到的值一致,仅想主卡执行时在回调内自行 判断 rank;提前 break / 抛异常不触发:for _ in StepsBar(val_loader, epoch=epoch, metrics=val_metrics, on_end=lambda m: save_if_best(m["acc"])): val_metrics.feed(args=(logits, labels), loss=loss, write=False)
tab 面板分区(train / val / test)与 step 隔离
StepsBar 传 tab(或 melog.scalar(..., tab=...))把记录分到面板独立
分区——记录名自动加前缀(train/loss),Web 面板按显式声明的分区把
卡片分到独立区块、垂直排列(不靠命名猜测);分区内仍按指标名分卡,
recall/class_0 式逐类命名照常合并为一张多系列卡片。分区随日志持久化,
历史日志重新加载时一并恢复。
各分区的 step 独立计数、互不影响:train 的 40 步用 train 分区的 x 0..39,val 的记录从 val 分区的 x 0 起(写 val 不推进 train 的步数), 面板左侧切换分区后各自独立展示:
def make_metrics():
return MetricGroup({
"loss": Mean(),
"lr": Last(),
**{f"recall/class_{c}": Recall(num_classes=K, class_index=c) for c in range(K)},
})
train_metrics = make_metrics() # → train/loss, train/lr, train/recall/class_0...
val_metrics = make_metrics() # 同一套定义挂到不同分区
for epoch in range(EPOCHS):
# 训练分区:只看 master 实时指标,reduce=False 跳过跨卡合并
for _ in StepsBar(train_loader, epoch=epoch, tab="train",
metrics=train_metrics, reduce=False):
train_metrics.feed(...)
# 验证分区:结果需对验证集跨卡合并(默认 reduce=True)
for _ in StepsBar(val_loader, epoch=epoch, tab="val", metrics=val_metrics):
val_metrics.feed(write=False) # epoch 末自动合并记录 + reset
- 分区是 StepsBar 的
tab=...显式属性而非命名约定:不传tab时记录进 默认序列(loss、recall/class_0等原有命名分组不受影响) - StepsBar 声明了
tab时,bar 内不带tab的melog.scalar(...)记录 也归入该分区 - 完整示例见
examples/tab_demo.py
feed 如何分发观测
metrics.feed(args=..., **scalars) 把一个 batch 的观测一次喂入,两类指标
分开传、各取所需。以
metrics = MetricGroup({"loss": Mean(), "macc": MaskedAcc()})
metrics.feed(args={"logits": logits, "labels": labels, "mask": mask},
loss=(loss, batch_size))
为例,一次 feed 内部的流转:
args=:观测型指标(如"macc"与所有内置分类指标)的观测, 单独成组——字典按键名对应各指标compute/prepare的形参 (推荐,形参多时更可读),自动分发给形参名匹配的指标,多余的键忽略; 元组按位置喂给未被注册名喂入的指标。 缺少必需形参才抛KeyError。**scalars:按注册名喂入的指标(Mean/Sum/Last/Count,如"loss"): 按注册名找同名键——取出loss=(loss, batch_size);是元组就展开为feed(loss, batch_size)加权累积,普通数值则等权。本 batch 没有同名键就跳过 (不累积也不报错)。
一句话:观测型指标的观测放 args,标量指标按注册名"点名取值"。两类规则
互不干扰,所以同一个 feed 调用可以同时喂两类指标;无主的多余观测两边都不收。
单独使用某个指标时规则一致:标量指标位置喂入 Mean().feed(value, count);
观测型指标具名或位置均可 MaskedAcc().feed(logits=..., labels=..., mask=...),
框架同样按 compute / prepare 形参名组装。
分类指标
内置 Accuracy / Precision / Recall / F1 / ConfusionMatrix,接口与基础指标一致,
feed(logits, labels) 直接接收模型输出与标签:
from melog import Accuracy, F1, MetricGroup, Mean, Precision
metrics = MetricGroup({
"loss": Mean(),
"acc": Accuracy(), # 二分类:一维得分按阈值 0.5 判定
"acc5": Accuracy(topk=5), # top-5 准确率(多分类)
"f1": F1(num_classes=10), # 多分类:二维 (N, K) logits 按行 argmax
})
# 验证集:write=False 不逐 batch 写曲线,epoch 末自动跨 GPU 合并记录
for logits, labels in StepsBar(val_loader, epoch=epoch, metrics=val_metrics):
# feed:观测型指标的观测放 args(元组按位置 / 字典按键名),
# 标量指标按注册名喂入(loss 自动按批次样本数平均)
val_metrics.feed(args=(logits, labels), loss=loss, write=False)
# 无需手动 scalar:StepsBar 结束时自动合并记录并 reset
Accuracy(topk=k):真实类别在前 k 个预测中即算正确Precision / Recall / F1的average:None(二分类=正类,多分类=macro)/"macro"/"micro"/"weighted"ConfusionMatrix的compute()返回矩阵(行=真实、列=预测),适合直接读取而非画曲线- 预测规则由
preds_from_logits实现,可传predictor=替换(如多标签、分割等自定义转换)
自定义指标
统一继承 Metric,按指标何时出值选择实现方式:
实时指标——只实现 compute():每次喂入立即用本批观测算出指标值,
形参名和个数完全由你定义,框架按形参名自动从 feed() 的观测中取值回调;
各 batch 结果按各自实际的样本数加权平均、跨 GPU 合并,全部由框架完成:
from melog import Metric
class MaskedAcc(Metric):
"""需要几个参数就声明几个,logits/labels 仅为示例。"""
def compute(self, logits, labels, mask):
hits = ((logits.argmax(-1) == labels) & mask).sum()
n = mask.sum()
return (hits / n, n) # 返回 (值, 观测数):按样本数平均出全局结果
# 训练循环里:位置或具名喂入均可,多余观测自动忽略
metric.feed(logits, labels, mask)
metric.feed(logits=logits, labels=labels, mask=mask)
compute返回(值, 观测数)元组:各 batch 按观测数(如样本数)平均(样本数不同时务必带上); 只返回 float 时各 batch 等权平均- 组合使用时交给
MetricGroup.feed(...)统一分发:
metrics = MetricGroup({"loss": Mean(), "macc": MaskedAcc()})
# 每个 batch:feed 把观测累积进各指标的内存状态并自动记录本卡实时值
# (观测型指标观测放 args,标量指标按注册名,Mean 自动按批次样本数平均)
for logits, labels, mask in StepsBar(loader, epoch=epoch, metrics=metrics):
metrics.feed(args={"logits": logits, "labels": labels, "mask": mask},
loss=loss)
# epoch 末无需任何手动调用:StepsBar 结束时自动跨 GPU 合并记录 + reset
不用 StepsBar 包裹时(如独立验证脚本)才需要手动落盘:
melog.scalar(metrics) 跨 GPU 合并记录,metrics.reset() 清零开启下一轮。
epoch 级指标——加实现 prepare():全局结果无法由各 batch 值按样本数加权平均还原时
(如 macro F1、AUC),每次喂入先用 prepare()"备料"——接收同样的观测,
返回本批次贡献的增量(数值 / 字典 / 列表),框架自动累积并跨 GPU 合并
(数值求和、字典按键合并、列表拼接);epoch 末把合并后的总量交给
compute() 算出全局结果:
from melog import Metric
class F1(Metric):
"""epoch 末才能计算的指标:累积混淆计数,末尾统一算。"""
def prepare(self, tp, fp, fn): # 每个 batch:本批次贡献的计数
return {"tp": tp, "fp": fp, "fn": fn}
def compute(self, tp, fp, fn): # epoch 末:由总量算出全局值
return 2 * tp / (2 * tp + fp + fn) if tp + fp + fn else float("nan")
# 使用(通常放进 MetricGroup / StepsBar 自动记录):
f1 = F1()
f1.feed(tp=2, fp=1, fn=0) # 位置或具名喂入均可
f1.result() # 跨 GPU 合并并计算(单进程直通)
多 GPU
代码无需修改:按你的分布式训练方式(torch.distributed 初始化完成),
每个 rank 进程各自跑同一份 melog 代码,melog.init 后库自动感知
分布式环境(未装 torch 或单进程运行则一切退化为本地直通,行为完全一致)。
自定义 Metric 时多卡对你透明,不需要写任何分布式代码。compute()
拿到的永远是框架合并好的全量,合并规则按指标类型:
实时指标(只实现 compute)——各卡的本批值按各自实际的样本数加权平均:
全局值 = Σ(各卡每批的 值 × 该批样本数) / Σ(各卡每批的 样本数)
即框架把每卡的 (值, 观测数) 对累加后相除——等价于把所有卡的样本
放到一起算,而非"各卡平均值的平均"。
epoch 级指标(prepare + compute)——各卡 prepare() 返回的
增量先在本地逐 batch 累积,epoch 末把各卡的本地增量跨卡合并,规则只有
三条(可递归组合):
| 增量形态 | 合并规则 | 例 |
|---|---|---|
| 数值 | 求和 | {"tp": 3} + {"tp": 2} → tp=5 |
| 字典 | 按键递归合并(值按同规则) | 两卡各自 {"(1,1)": 2, "(1,0)": 1} 与 {"(1,1)": 1} → {"(1,1)": 3, "(1,0)": 1} |
| 列表 / 元组 | 拼接 | 两卡各 64 个 (得分, 标签) 对 → 128 个 |
三条规则都与合并次序无关(求和与拼接可结合),所以"逐 batch 累积"和
"跨卡合并"用的是同一套规则;epoch 末合并后的总量按 compute() 的
形参名传入(字符串键字典展开为关键字参数,其余作为单个位置参数)。
由此带来的性质:
- 不要求各卡数据同构:各卡观测到的类别、样本数可以不同,计数按 键自动对齐;逐样本对自动拼接,结果与单卡全量计算一致。
- 唯一的纪律:所有 rank 以相同顺序喂入(正常训练代码天然满足)。
记录交给
StepsBar/MetricGroup自动完成即自动满足;手动melog.scalar(...)时所有 rank 执行同一位置即可。 - 验证正确性最简单的办法:单进程跑一遍的
result()应等于多卡 全局值(合并规则保证)。
API
melog.init(...)
| 参数 | 默认 | 说明 |
|---|---|---|
log_dir |
./melog_runs |
日志保存路径(即 run 目录,日志直接落在其中);重跑同一目录即断点续训 |
web_port |
随机空闲端口 | Web 监听端口(web_host 默认 127.0.0.1,地址启动时自动打印,也可读 melog.current().web_url) |
enable_web |
True |
启动 Web 服务(仅 rank0) |
enable_progress |
True |
启用控制台进度条 |
reduce_op |
"mean" |
多 GPU 合并方式 |
flush_every |
1 |
每 N 次 scalar 落盘一次 |
project |
log_dir 末级目录名 |
覆盖项目名 |
主要方法
scalar(metrics, advance=0)— 记录一批指标(dict 或 MetricGroup,后者跨 GPU 合并由内部完成);坐标由StepsBar自动管理(epoch 绑定 + 内部计步),调用频率即记录粒度(见上文)MetricGroup(metrics, category=None)— 具名指标集合(from melog import MetricGroup),feed(...)统一分发观测、跨 GPU 合并;category=...传大类别(train / val / test),Web 面板按类别垂直分区(见上文)image(name, data, caption=None)/audio(name, data, sr=22050, ...)— 记录图像 / 音频,自动附着最近一次记录位置,Web 端页签展示(见上文)StepsBar(iterable, epoch=None, tab=None, metrics=None, reduce=True, on_end=None)— tqdm 风格训练进度条(from melog import StepsBar,模块级melog.stepsbar(...)等价),epoch 循环必须用它包裹:包裹可迭代对象即自动推进,scalar()指标实时显示在条上;epoch=...绑定当前 (分区, epoch) 并统一管理坐标;tab=...声明面板分区(各分区 step 独立计数);metrics=...传入 MetricGroup 时 bar 实时显示本卡本地值(feed 零通信刷新),迭代自然结束自动 gather 全局值合并记录并重置组内指标(reduce=False只记 master 实时值、不做合并;提前 break / 异常不触发);on_end=...在合并记录后收到合并后的指标字典(见上文)- 允许嵌套(如训练 bar 内嵌验证 bar):内部以栈管理,
current_bar()返回栈顶即当前环境;scalar()的 postfix 与advance自动作用于栈顶,下层 bar 暂停渲染(数据照常累计),栈顶关闭后自动恢复下层渲染;提前 break / 抛异常时 bar 自动出栈(如需立即定稿可close()或用 with),否则等引用释放时兜底 current_bar()— 当前栈顶进度条(无打开的 bar 时None);深层函数需要手动推进 / 读数 / 写 postfix 时取它,免层层传参log / success / error / warn— print 风格控制台消息([MM-DD HH:MM:SS][文件名]前缀 + 图标 + 彩色文字),print被拦截改走log();多 GPU 下默认仅 rank0 输出,all_ranks=True时各卡都输出(见上文)- 收尾无需手动调用:进程退出时经 atexit 自动落盘剩余指标、定稿进度条、停 Web、还原 print
- 全局共享:
melog.init(...)创建活动实例后,模块级melog.scalar(...)等可在任意位置直接调用(见上文)
进度条显示的指标可通过环境变量 MELOG_DISABLE_PROGRESS=1 全局关闭。
CLI 快速查看
安装后可直接用命令行查看历史日志,自动打开浏览器:
melog F:/runs/exp1/metrics-20260903_101010.melog # 指定会话日志(自动合并同目录全部会话)
melog F:/runs/exp1 # 指定 run 目录(合并其全部会话文件)
melog # 缺省在 ./melog_runs 中查找
melog F:/runs/exp1 --port 9000 --no-browser # 自定义端口 / 不开浏览器
开发
pip install -e ".[dev]"
pytest tests -q
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file melog-1.5.2-py3-none-any.whl.
File metadata
- Download URL: melog-1.5.2-py3-none-any.whl
- Upload date:
- Size: 455.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.10.20
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d086bd6067424b50d47ea6f0f32f546b8b4c447e2817332d418cb32b346791eb
|
|
| MD5 |
1e4248a551a2912cef5df946188edf6b
|
|
| BLAKE2b-256 |
437eea8abf51ed6a1d217fb83d7a28ed9b2dd8339c6bea0c29975564ac8abe9d
|