ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

XML-CSV-TFRecord:目标检测数据转换完整流程与避坑指南

XML-CSV-TFRecord:目标检测数据转换完整流程与避坑指南 简介面向需要将标记语言数据转换为深度学习模型可用格式的开发者这套脚本工具提供了从XML到CSV、再到TFRecord的完整转换方案。压缩包共含2个Python脚本整体体积仅3KB轻量无依赖第一个脚本基于ElementTree库解析XML文档借助pandas输出CSV中间文件方便人工校对与二次清洗第二个脚本将每行样本封装为TensorFlow的Example协议缓冲区并写入TFRecord使后续tf.data.TFRecordDataset可高效批量读取大幅减少IO开销。与直接将XML输入训练相比TFRecord格式支持压缩、分片与缓存能有效缩短模型调参时的数据加载时间。整套流程覆盖数据清洗、格式转换与序列化三个关键环节能直接嵌入现有TensorFlow数据管道脚本也封装了常用参数使用者只需修改输入输出路径即可运行。目前已有250人学习下载特别适合正在搭建目标检测、OCR等视觉任务训练流程的Python开发者也适用于需要将第三方XML标注迁移到TensorFlow生态的数据工程师。1. scripts(xml-csv-tfrecord).rar从标注文件到TFRecord的一条标准训练数据链路拿到scripts(xml-csv-tfrecord).rar这组脚本时你多半正卡在数据准备那一环图像分类或目标检测项目里样本标注是VOC风格或自研结构的XML训练框架却要求喂TFRecord格式。这套脚本把“XML解析成CSV、CSV再统一转TFRecord”做成两条可独立执行的小工具链中间用CSV做衔接。CSV的好处是随时可以用pandas读取csv文件看一眼内容字段摊平后做清洗和过滤远比直接改XML省事。下面按“格式选型→XML转CSV→CSV转TFRecord→踩坑→写后验证”的顺序展开命令和参数都按可直接复跑的方式写。2. 为什么是xml-csv-tfrecord三种格式在训练数据流水线里的分工这一章先把“为什么要按这个顺序转”讲透。实际项目里你也可以从XML直接构造TFRecord但多数有经验的工程师会在中间加一层CSV这不是多此一举而是给流水线加了一个可检查、可断点的缓存。2.1 XML、CSV、TFRecord三种格式的定位格式结构谁在消费典型问题XML树形层级标注工具、人工查看批量解析慢XPath写起来啰嗦CSV扁平的表格pandas、Excel、MySQL丢失层级关系同一张图多目标要拆多行TFRecord二进制流TensorFlow训练人眼不可读出错很难直接DebugXML擅长表达嵌套结构一个annotation节点底下挂着size、object、bndbox语义很清楚这也是标注工具愿意用它存储的原因。但树形结构在批量处理时很吃亏你要先定位到object节点再往下找bndbox每张图多一个目标就多一段重复代码而且不同标注工具导出的字段命名还不一样解析脚本经常要按工具维护版本。CSV则把树拍平成一张表每一行是一个目标框filename、width、height重复出现在多行里。这种冗余是有意为之换来的是能用一条df[df[xmax] df[xmin]]做全量过滤能直接拖进WPS表格肉眼检查坐标是否越界也能用MySQL的LOAD DATA导入csv文件到数据库做统计。数据团队拿到CSV不需要懂XML结构就能开始干活。TFRecord是TensorFlow官方推荐的训练输入格式把样本序列化成二进制配合tf.data.TFRecordDataset读取能做到边读边解码、自动shuffle和预取。它解决的问题是训练阶段的I/O瓶颈。但TFRecord本身没有schema字段全靠写入方和读取方约定一致一旦没对齐训练时拿到的是错的shape或者错的数据类型排查成本极高。所以TFRecord应该被视为“接近最终产物”的一层而不是第一手可编辑的数据。2.2 中间层为什么选CSV而不是XML直转TFRecord常见做法是在XML和TFRecord之间加CSV理由有三个。第一可观测性。训练样本出问题时工程师第一反应是“看看原始标注对不对”。XML不是不能看但上百个标签文件堆在目录里用文本编辑器逐个翻效率太低。转成CSV后一行一个框class_name分布可以直接df[class_name].value_counts()统计哪个类别样本量少一目了然。第二断点续跑。xml到csv这一步往往是最容易踩坑的碰到一个坏文件就会中断。把已解析的结果落到CSVcsv这一步跑完后后面CSV转TFRecord即使反复调试也不需要重新解析XML。对几千张图的小项目差距不明显跑到几万张图时这个“中间缓存”能省下几十分钟甚至几个小时。第三格式中立。CSV不是TensorFlow的私有格式训练框架从TFRecord换成别的方案时数据清洗和统计逻辑还能复用。反过来如果一开始就把所有信息直接写死成TFRecord换框架时只能重新解析XML。CSV在数据链路里更像一个“半成品”存档点。所以这条链路的定位是XML保留原始标注的完整结构CSV负责承接清洗与统计TFRecord只负责训练阶段的高效读取。每一层都有明确的职责改动其中一层不影响另外两层。2.3 搭这套脚本需要的最小工具栈针对scripts(xml-csv-tfrecord)这套脚本我一般会准备以下环境工具版本建议用途Python3.8以上运行脚本xml.etree.ElementTree标准库自带解析XML无需额外安装pandas1.3以上读取csv、清洗、统计tensorflow2.6以上构造tf.train.Example写TFRecordchardet可选识别XML文件编码处理乱码xml.etree.ElementTree是标准库解析普通标注文件足够用不需要为这层引入lxml。lxml在XPath复杂查询和大文件场景更有优势但这里每个XML只有几十个节点标准库的解析速度不会有明显差别。tensorflow建议装CPU版就够了因为写TFRecord不需要GPUtensorflow-cpu体积小很多。环境管理的坑在Windows下很常见。如果你用PyCharm或者VSCode的虚拟环境运行时注意当前解释器是不是你装依赖的那个Python。有个很容易忽视的细节直接用d:\pyth\.venv\scripts\python.exe d:\pyth\jb\20260923.py执行脚本时脚本里import pandas报No module named pandas多半是依赖装到了另一个环境。后面第5章会专门展开这个问题。3. 把XML标签解析成CSV字段设计、解析脚本与参数说明这章进入第一个可执行环节。目标很明确把单个目录下的XML标注文件解析成一张扁平的CSV表之后再清洗、统计、过滤都以这张表为基准。3.1 先认清输入XML结构、编码与常用查看方式写解析脚本之前先用编辑器打开一个XML文件确认结构。xml文件怎么打开和编辑直接用VSCode、Notepad或者系统记事本都能看带XML插件的编辑器会有节点高亮找字段方便很多。典型的VOC检测标注长这样annotation folderimages/folder filename000001.jpg/filename size width1920/width height1080/height depth3/depth /size object namecar/name bndbox xmin100/xmin ymin50/ymin xmax800/xmax ymax600/ymax /bndbox /object /annotation这个结构里filename是图片文件名size是图宽高object的name是类别bndbox是目标框坐标。注意一个XML里可能有多个object节点解析脚本要把每个object展开成CSV里的独立一行。打开XML后第一件事是看文件声明的encoding。有的标注工具声明encodingUTF-8实际内容却用了GBK特别是中文字段出现时解析到一半就会抛ParseError。这个坑在第5章会详细讲这里需要养成习惯批量解析前先抽查5到10个文件确认编码统一。3.2 解析脚本逐目录、逐文件抽取bbox到CSV下面这段脚本是完整可跑的输入一个标注XML目录输出一个CSV文件import csv import os import xml.etree.ElementTree as ET CSV_FIELDS [ image_filename, width, height, class_name, xmin, ymin, xmax, ymax ] def parse_one_xml(xml_path): 解析单个XML返回多行标注记录 tree ET.parse(xml_path) root tree.getroot() filename root.findtext(filename) width int(root.findtext(size/width)) height int(root.findtext(size/height)) rows [] for obj in root.iter(object): bbox obj.find(bndbox) rows.append([ filename, width, height, obj.findtext(name), int(bbox.findtext(xmin)), int(bbox.findtext(ymin)), int(bbox.findtext(xmax)), int(bbox.findtext(ymax)), ]) return rows def xml_dir_to_csv(xml_dir, csv_path): 遍历目录下所有XML解析后写入CSV with open(csv_path, w, newline, encodingutf-8) as f: writer csv.writer(f) writer.writerow(CSV_FIELDS) for file_name in sorted(os.listdir(xml_dir)): if not file_name.lower().endswith(.xml): continue file_path os.path.join(xml_dir, file_name) try: rows parse_one_xml(file_path) except ET.ParseError as e: print(f[skip] {file_name}: {e}) continue writer.writerows(rows) print(fparsed XMLs - {csv_path}) if __name__ __main__: # 用法示例python xml_to_csv.py --xml_dir ./annotations --csv_path ./labels.csv import argparse parser argparse.ArgumentParser() parser.add_argument(--xml_dir, requiredTrue, helpXML标注所在目录) parser.add_argument(--csv_path, requiredTrue, help输出CSV路径) args parser.parse_args() xml_dir_to_csv(args.xml_dir, args.csv_path)脚本的逻辑分三层最里层的parse_one_xml负责单个XML的字段抽取root.iter(object)能遍历所有object节点哪怕XML里嵌套了object的子节点也能稳定找到。中间层的xml_dir_to_csv负责目录遍历用os.listdir过滤出.xml后缀的文件逐个解析并逐行写入CSV。最外层用argparse暴露参数方便命令行调用。这里有两个容易改错的参数。encodingutf-8决定了输出CSV的编码后续pandas读取csv文件时如果不指定编码默认也会按UTF-8处理两边保持一致才不会乱码。newline是csv模块的固定搭配不加的话在Windows上写出来的CSV每两行之间会多一个空行这个空行会让pandas读进来时出现大量NaN行。另外跳过解析失败的文件时print里同时打印文件名和异常信息这样能看到是哪一批XML出了问题。脚本不会因为一个坏文件中断这是批量脚本的基本要求。3.3 字段映射的四个约定不提前归一化坐标字段设计决定了后续TFRecord里特征怎么组织。我习惯在这层坚持四个约定。第一坐标不归一化。CSV里直接存像素坐标xmin、ymin、xmax、ymax归一化放到TFRecord转换阶段再算。原因是CSV这一层要能被人工检查存原始像素值更容易对照原图判断是否正确如果在XML转CSV时就归一化检查CSV的人还要拿width、height反推像素值凭空多一步。第二类别保留原始字符串。不要在这里把car映射成0、1这样的数字ID。数字映射容易在类别集合变动时出错新增一个类别就要重跑整个CSV。等CSV转TFRecord时再根据class_name建映射表或者直接在训练脚本里处理灵活性更高。第三同一张图多个目标拆成多行。CSV每一行只记录一个目标的bbox和类别filename、width、height重复出现。这种冗余在早期阶段是值得的因为groupby或者drop_duplicates这类操作能轻松还原图片维度的信息反过来把多目标塞进一行反而难处理。第四图片路径不在这一层拼接。有些项目里XML只在folder里写了相对路径而图片存在另一个目录。路径拼接逻辑和具体项目的目录布局强相关放在CSV转换这一步会污染通用脚本。常见做法是只在CSV里存filename图片根目录在训练脚本里配置。4. 把CSV转成TFRecord构造Example的脚本与多文件写入策略CSV准备完成后数据已经具备了可检查、可过滤的基础。这一章解决最后一步把CSV内容变成TensorFlow能高效读取的TFRecord二进制文件。4.1 TFRecord里到底存了什么Example与Feature的对应关系TFRecord文件里存的是tf.train.Example的序列化字节。每个Example是一个Features字典键是特征名值是Feature对象。Feature按数据类型分成三种容器BytesList存字符串或编码后的二进制、Int64List存整数、FloatList存浮点数。对应到CSV的字段我的特征组织方式是CSV字段TFRecord特征名类型容器image_filenameimage/filenameBytesListwidthimage/widthInt64Listheightimage/heightInt64Listclass_nameimage/object/class/textBytesListxminimage/object/bbox/xminFloatListyminimage/object/bbox/yminFloatListxmaximage/object/bbox/xmaxFloatListymaximage/object/bbox/ymaxFloatList特征名带image/这样的前缀是TensorFlow数据集常见命名习惯。好处是后续做迁移学习或引入预训练模型时特征名冲突的可能性小。坐标为什么用FloatList而不用Int64List因为很多模型训练时要求归一化后的浮点坐标一旦后面要改特征类型不用重写整个TFRecord生成脚本。4.2 生成脚本pandas读取csv并通过tf.train.Example写入TFRecord下面是完整的生成脚本输入上一章输出的CSV输出TFRecord文件import argparse import pandas as pd import tensorflow as tf def build_example(row): 把CSV的一行转换成tf.train.Example feature { image/filename: tf.train.Feature( bytes_listtf.train.BytesList(value[row[image_filename].encode(utf-8)]) ), image/width: tf.train.Feature( int64_listtf.train.Int64List(value[int(row[width])]) ), image/height: tf.train.Feature( int64_listtf.train.Int64List(value[int(row[height])]) ), image/object/class/text: tf.train.Feature( bytes_listtf.train.BytesList(value[row[class_name].encode(utf-8)]) ), image/object/bbox/xmin: tf.train.Feature( float_listtf.train.FloatList(value[float(row[xmin])]) ), image/object/bbox/ymin: tf.train.Feature( float_listtf.train.FloatList(value[float(row[ymin])]) ), image/object/bbox/xmax: tf.train.Feature( float_listtf.train.FloatList(value[float(row[xmax])]) ), image/object/bbox/ymax: tf.train.Feature( float_listtf.train.FloatList(value[float(row[ymax])]) ), } return tf.train.Example(featurestf.train.Features(featurefeature)) def csv_to_tfrecord(csv_path, record_path): 读取CSV逐行构造Example写入TFRecord df pd.read_csv(csv_path) valid df[(df[xmax] df[xmin]) (df[ymax] df[ymin])] skipped len(df) - len(valid) print(fread {len(df)} rows, skipped {skipped} invalid boxes) with tf.io.TFRecordWriter(record_path) as writer: for row in valid.itertuples(indexFalse): example build_example(row._asdict()) writer.write(example.SerializeToString()) print(fwrote {len(valid)} examples to {record_path}) if __name__ __main__: parser argparse.ArgumentParser() parser.add_argument(--csv_path, requiredTrue, help输入CSV路径) parser.add_argument(--record_path, requiredTrue, help输出TFRecord路径) args parser.parse_args() csv_to_tfrecord(args.csv_path, args.record_path)这里最关键的细节是row._asdict()。pandas的itertuples返回的是命名元组直接用row.image_filename也能取字段但命名元组在字段名和Python关键字冲突时会报错。_asdict()把它转成字典再传给build_example特征名和CSV列名的耦合只出现在这一处后续改列名只需要改build_example。写入方式是tf.io.TFRecordWriter配合with语句。写入循环里没有做任何复杂计算因为构造Example本身不费时真正的性能瓶颈在磁盘I/O。如果数据集特别大可以在循环外加tqdm显示进度避免脚本看起来像是卡住了。这里也体现了pandas读取csv文件的第二个作用在写入TFRecord之前顺手做一次数据合法性检查。第3章里CSV已经存了原始坐标这里用xmax xmin和ymax ymin两个条件滤掉宽或高为0的坏标注。这类脏数据如果直接写进TFRecord训练时要么loss变成NaN要么在NMS阶段引发奇怪的维度错误。4.3 多文件、大样本的写入策略分片、shuffle与数量核对CSV和TFRecord的转换不是只能输出一个文件。实际项目里几万张图片生成一个几百MB甚至上GB的TFRecord文件读取时需要占用连续内存反而影响tf.data的预取效率。常见做法是按数据来源或样本量分片。如果CSV里有一个splits列可以按train、val、test分组每组写一个TFRecord如果没有现成的分片字段就按行数切分每5000行写一个shard文件文件名带-00000-of-00005这种序号TensorFlow的tf.data.Dataset能直接读取文件名模式。shuffle顺序要提早在CSV层面做。pd.read_csv读进来后调用df.sample(frac1).reset_index(dropTrue)打乱后再写TFRecord。如果顺序不打乱相同类别的样本会集中出现在文件里训练时每个batch的类别分布波动会很大。写出多个TFRecord后核对数量是必做的一步。我在生产环境里会维护一个简单的文本日志记录CSV行数、滤掉的行数、写出的example数三个数字对得上才认为转换成功。后面第5章的损坏检测也是基于这个数量记录来定位问题。脚本具体实现上如果样本太大一次性读CSV内存吃紧就用pd.read_csv的chunksize参数分批读每批10000行写完一个临时文件碎片再合并但对于大多数中小型项目整体读进内存反而更简单可靠。5. xml-csv-tfrecord转换避坑5个高频问题从现象到解决数据转换脚本写起来不难跑起来总能碰到各种幺蛾子。这章把我遇到过的高频问题按“现象→原因→解决”写出来每条都是实际踩过的可以直接对照自己的报错排查。5.1 XML解析第一行就报ParseError编码声明和实际内容不一致现象运行第3章的解析脚本某些XML文件直接抛xml.etree.ElementTree.ParseError报错位置往往在文件头几行。单独打开这个XML用记事本看内容显示一堆乱码。原因文件开头的?xml version1.0 encodingUTF-8?是标注工具写死的但实际内容用了GBK或者GB2312编码尤其是中文字段名或中文类别名出现时。ElementTree严格按声明解析碰到不符合UTF-8规则的字节就抛异常。解决脚本里不直接用ET.parse(path)改成先读字节再解码import xml.etree.ElementTree as ET def parse_xml_with_fallback(xml_path): raw open(xml_path, rb).read() for encoding in (utf-8, gbk, gb2312): try: text raw.decode(encoding) return ET.fromstring(text) except (UnicodeDecodeError, ET.ParseError): continue raise ValueError(fcannot decode {xml_path})这个兜底逻辑在utf-8解码失败时自动尝试gbk两个常用中文编码都试过还不行才报错。整套脚本会多花一点解码时间但换来的是批量解析不中断。如果项目里的XML来源固定也可以先用chardet识别一遍编码统一转成UTF-8再处理不过对小数据集用上面的兜底函数更省事。5.2 多个CSV合并后样本数翻倍表头被当成数据行现象多个批次导出的CSV用pandas合并后len(df)比各文件行数之和多出一大截训练集和验证集之间出现大量重复样本。原因合并时没有处理表头。常见场景是用pd.concat([df1, df2])拼接时两个CSV都有image_filename开头的表头行pandas默认把表头当作数据行导致重复样本占了一半。解决读取CSV时显式指定header0或者合并前过滤掉与表头完全相同的行import pandas as pd frames [] for path in [labels_part1.csv, labels_part2.csv]: df pd.read_csv(path, header0) frames.append(df) merged pd.concat(frames, ignore_indexTrue) merged merged.drop_duplicates(subset[image_filename, class_name, xmin, ymin, xmax, ymax]) merged.to_csv(labels_all.csv, indexFalse)注意drop_duplicates的subset要包含所有定位样本的字段。只按image_filename去重是不行的同一张图多个目标对应多行会导致非重复目标被误删。这个过滤在CSV合并后做一次能在进入TFRecord之前把脏数据挡在门外。5.3 写入TFRecord后训练报“坐标越界”bbox出现负数或零宽现象训练开始后不久损失函数出现NaN或者在计算IoU时报width must be positive打印出的bbox坐标有负数xmin大于xmax。原因CSV里混入了标注错误的坏框。有些标注工具在目标完全在画面外时仍保留object节点坐标可能超出width和height甚至出现xmin xmax这种零宽框。第4章脚本里的过滤条件覆盖了零宽框但没覆盖负坐标当xmin是-50时xmax xmin仍然成立。解决在CSV转TFRecord之前做一次边界钳制df[xmin] df[xmin].clip(lower0) df[ymin] df[ymin].clip(lower0) df[xmax] df[xmax].clip(upperdf[width]) df[ymax] df[ymax].clip(upperdf[height]) valid df[(df[xmax] df[xmin]) (df[ymax] df[ymin])]clip把坐标限制在图片范围内再用比较条件过滤掉钳制后依然无效的框。这里有个取舍直接删除越界框会损失样本量而钳制能保留目标的大部分区域对检测任务来说通常更友好。钳制后样本数量不变但坐标都合法了。5.4 用venv里的Python执行脚本时No module named pandas解释器路径与环境错配现象命令行里明明用d:\pyth\.venv\scripts\python.exe d:\pyth\jb\20260923.py跑脚本traceback却是ModuleNotFoundError: No module named pandas但PyCharm里运行同一个脚本又一切正常。原因脚本所在目录或终端激活的虚拟环境与PyCharm配置的项目解释器不是同一个venv。Windows下尤其容易出现PyCharm创建了一个.venv但终端里直接调用的是另一个路径下的python.exe依赖装在了PyCharm那个环境里。解决统一用一个解释器。先确认当前用哪个Pythond:\pyth\.venv\scripts\python.exe -c import sys; print(sys.executable) d:\pyth\.venv\scripts\python.exe -m pip list如果pip list里确实没有pandas就用同一个解释器装依赖d:\pyth\.venv\scripts\python.exe -m pip install pandas tensorflow-cpu装完后再次执行原脚本就不会再报No module named。关键是用python -m pip而不是直接pip install这样能保证包安装到当前解释器对应的环境里不会装到系统全局Python。Windows上这个坑隐蔽在“明明安装了却找不到”的现象里本质就是解释器路径错配。5.5 TFRecord写到一半进程被杀训练读取时文件损坏现象转换脚本跑了一段时间被手动终止或断电重新开始训练时tf.data.TFRecordDataset读取报OpError或DataLossError错误信息指向文件末尾的truncated记录。原因TFRecordWriter写文件不是原子操作进程在写第N个Example时中断文件尾部缺少完整的长度校验字段。TensorFlow读取时会按前4字节的长度前缀切分记录读到残缺的尾部字节就判定文件损坏。解决转换脚本改成“先写临时文件成功后重命名”并写完后自校验import os import tempfile tmp_path record_path .tmp with tf.io.TFRecordWriter(tmp_path) as writer: for row in valid.itertuples(indexFalse): writer.write(build_example(row._asdict()).SerializeToString()) os.replace(tmp_path, record_path)os.replace在Windows和Linux上都是原子操作写临时文件期间即使进程被杀原TFRecord文件也保持完好。写完重命名后再加一道自校验用tf.data.TFRecordDataset遍历一次文件统计example数量并与写入计数对比count sum(1 for _ in tf.data.TFRecordDataset(record_path)) print(fread back {count} examples)这里两个数字必须一致。不一致说明写入过程仍然有问题需要重新生成。养成这个习惯后训练阶段基本不会再遇到TFRecord损坏的报错。6. 写后读回用tf.data验证TFRecord是否真的能用TFRecord生成后直接扔进训练脚本不是最稳妥的做法。我会先做两个验证确认文件格式和数据内容都没问题再让训练脚本去加载。第一个验证是解析结构是否正确用tf.data.TFRecordDataset配合tf.io.parse_single_example读取前几条数据import tensorflow as tf feature_description { image/filename: tf.io.FixedLenFeature([], tf.string), image/width: tf.io.FixedLenFeature([], tf.int64), image/height: tf.io.FixedLenFeature([], tf.int64), image/object/class/text: tf.io.FixedLenFeature([], tf.string), image/object/bbox/xmin: tf.io.FixedLenFeature([], tf.float32), image/object/bbox/ymin: tf.io.FixedLenFeature([], tf.float32), image/object/bbox/xmax: tf.io.FixedLenFeature([], tf.float32), image/object/bbox/ymax: tf.io.FixedLenFeature([], tf.float32), } def parse_fn(example_proto): return tf.io.parse_single_example(example_proto, feature_description) ds tf.data.TFRecordDataset([train.tfrecord]) ds ds.map(parse_fn) for sample in ds.take(5): print(sample[image/filename].numpy().decode(utf-8)) print(sample[image/object/bbox/xmin].numpy())take(5)只取5条样本打印出来重点看filename是否和CSV里的原始值一致坐标类型是否浮点数。如果这里特征名或者类型不匹配parse_single_example会直接抛错错误信息会告诉我们哪个字段没对上比训练跑到一半才报错好处理得多。第二个验证是统计类别分布确认数据没有在转换过程中丢东西。用第4章生成脚本里记录的wrote N examples和读回计数做比对再按class_name分组计数和CSV里的value_counts()结果核对。如果两边对不上回头检查过滤条件是不是误删了合法样本。验证全通过后再连到训练循环里跑一个极小的step比如只取200条样本训练1到2步确认model.fit或者自定义训练循环能正常消费dataset。这一步能暴露TFRecord之外的问题比如图片路径读不到、归一化计算溢出这些问题光看TFRecord数据是发现不了的。我自己的习惯是每次转换完把CSV行数、滤掉行数、TFRecord条数、类别分布四组数字写进一个data_report.txt下次项目回访或复现实验时直接翻文件就知道数据版本发生了什么变化。这个习惯救过我几次样本量变了但模型指标下降时翻日志就能定位是不是数据清洗规则改错了。数据转换脚本往往是一次性工具但它产出的数据会被反复消费值得在验证环节多花十分钟。希望帮到你。本文还有配套的精品资源点击获取
返回列表