LLM系列:1.python入门:15.JSON 数据处理与操作

JSON 数据处理与操作

一. 认识JSON与Python类型的映射关系

JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式。

在 Python 中,我们不需要安装第三方库,直接导入内置的 json 模块即可:

python 复制代码
import json

核心概念:JSON 本质上只是一串"带有特定格式的文本字符串"。在代码中操作时,我们需要将其与 Python 内存中的数据结构(字典、列表)相互转换。

JSON 数据类型 对应的 Python 数据类型
object ( {} ) dict (字典)
array ( [] ) list (列表)
string ( " " ) str (字符串,JSON 强制要求双引号)
number intfloat (整型或浮点型)
true / false True / False (布尔值,Python首字母大写)
null None (空值)

二. 前置知识:Python 原生文件操作 (open & with...as)

在将数据写入磁盘或从磁盘读取之前,我们必须先通过操作系统"打开"目标文件,建立数据传输管道。在 Python 中,这通常由内置的 open() 函数配合 with ... as 语法来完成。

1. open - 打开文件对象

作用:在内存和本地磁盘文件之间建立一条连接,返回一个可操作的文件对象。注:使用纯 open() 打开文件后,程序使用完毕时必须显式调用 .close() 关闭文件,否则会占用系统资源甚至导致文件损坏!

python 复制代码
open(file, mode='r', encoding=None)

参数:

  • 文件路径 (file): 需要打开的目标文件路径(str 或 Path 对象)。
  • 操作模式 (mode): 指定打开文件的目的(str)。深度学习中最常用的有:
    • 'r': 只读模式(默认)。文件必须存在,用于读取数据。
    • 'w': 覆盖写入模式。文件不存在则创建,文件若存在则完全清空原内容再写!
    • 'a': 追加写入模式。文件不存在则创建,存在则在文件最末尾继续追加内容。
  • 字符编码 (encoding): 指定文本的编解码方式。处理含中文或特殊符号的数据集时,强烈推荐固定设为 'utf-8'(str)。

返回值:

  • 成功: 返回一个可供读写操作的文件指针对象(通常为 TextIOWrapper)。

示例:

python 复制代码
# 传统写法(极不推荐,极其容易忘记 close 导致文件被占用)
f = open("test.txt", "w", encoding="utf-8")
f.write("Hello World!")
f.close()  # 必须手动执行这一句!

2. with ... as - 优雅的上下文管理器

作用:with 语句用于包装带有生命周期管理的对象(如文件读写、数据库连接)。它的核心魅力在于:无论缩进的代码块内部是否发生报错崩溃,只要代码脱离了 with 的缩进层级,Python 都会百分之百自动帮你执行 .close() 关闭并保存文件!

python 复制代码
with open(file, mode, encoding) as f:
    # 在这里的缩进块内对文件 f 进行读写操作
    pass
# 缩进结束,f 自动关闭

参数:

  • 无特定函数参数,这是 Python 的语法关键字。as 后面紧跟的是给这个文件对象起的变量名(科研代码中习惯简写为 ffilefin / fout)。

返回值:

  • 无直接返回值。

示例:

python 复制代码
# 现代标准写法(极度推荐,安全且优雅)
with open("test.txt", "w", encoding="utf-8") as f:
    f.write("Hello World!")
    # 不需要写 f.close(),缩进结束时系统自动安全关闭文件

三. 内存中:字符串与对象的相互转换 (dumps & loads)

带有 s (代表 string) 的方法,用于在内存中将"Python 对象"和"JSON 格式的字符串"相互转换。

1. json.dumps - 将对象序列化为 JSON 字符串

作用:将 Python 对象(如字典、列表)转化为 JSON 格式的字符串。常用于打印输出或准备写入文件。

python 复制代码
json.dumps(obj, ensure_ascii=True, indent=None)

参数:

  • 对象 (obj): 需要被转换的 Python 对象(通常是 dictlist)。
  • 保证ASCII (ensure_ascii): 【极其重要】 如果数据中包含中文,必须设置为 False!否则中文会被转码为 \uXXXX 格式的乱码!(bool,默认为 True)
  • 缩进 (indent): 格式化输出。指定缩进的空格数,让打印出来的 JSON 具有美观的层级结构(int 或 None)。

返回值:

  • 成功: 返回一个符合 JSON 规范的字符串(str)。

示例:

python 复制代码
data = {"instruction": "你好", "output": "世界"}

# 默认转换 (中文会变乱码,且没有换行)
res1 = json.dumps(data) 
# '{"instruction": "\\u4f60\\u597d", "output": "\\u4e16\\u754c"}'

# 深度学习常用标准参数:不转译中文,且缩进 4 格对齐
res2 = json.dumps(data, ensure_ascii=False, indent=4)
# {
#     "instruction": "你好",
#     "output": "世界"
# }
print(type(res2)) # <class 'str'>

2. json.loads - 将 JSON 字符串反序列化为对象

作用:将符合 JSON 语法的文本字符串,解析为 Python 内存中的真实对象(如字典)。

python 复制代码
json.loads(s)

参数:

  • 字符串 (s): 包含合法 JSON 数据的文本字符串(str)。

返回值:

  • 成功: 返回解析后的 Python 对象(通常是 dict 或 list)。

示例:

python 复制代码
json_str = '{"model": "llama-3", "parameters": 70}'

# 解析回 Python 字典
parsed_data = json.loads(json_str)

print(parsed_data["model"])  # 输出: llama-3
print(type(parsed_data))     # <class 'dict'>

四. 磁盘上:文件的读取与写入 (dump & load)

不带 s 的方法,必须配合上文提到的 open() 文件指针使用,直接将对象塞入文件,省去了手动读写字符串的步骤。

1. json.dump - 将对象直接写入 JSON 文件

作用:将 Python 对象直接序列化并写入到本地 .json 文件中。

python 复制代码
json.dump(obj, fp, ensure_ascii=True, indent=None)

参数:

  • 对象 (obj): 需要保存的 Python 数据对象(Any)。
  • 文件指针 (fp): 通过 with open(..., 'w') 打开的文件对象(TextIOWrapper)。
  • 保证ASCII (ensure_ascii) / 缩进 (indent): 用法与 dumps 完全一致。

返回值:

  • 成功: 无返回值(NoneType),数据被安全保存在本地。

示例:

python 复制代码
config = {"learning_rate": 2e-5, "batch_size": 32, "model_name": "Qwen-7B"}

# 使用 with open 语法安全地打开文件写入
with open("training_config.json", "w", encoding="utf-8") as f:
    json.dump(config, f, ensure_ascii=False, indent=4)

2. json.load - 直接从 JSON 文件读取对象

作用:读取本地 .json 文件的内容,并直接解析为 Python 对象。

python 复制代码
json.load(fp)

参数:

  • 文件指针 (fp): 通过 with open(..., 'r') 打开的文件对象(TextIOWrapper)。

返回值:

  • 成功: 返回解析后的 Python 对象(通常是 dict 或 list)。

示例:

python 复制代码
# 读取刚才保存的配置文件
with open("training_config.json", "r", encoding="utf-8") as f:
    loaded_config = json.load(f)

print(loaded_config["learning_rate"])  # 2e-05

五. 深度学习科研特供:JSONL 文件处理

在大模型训练(如 SFT 微调)时,极少使用标准的单个大 .json 文件。 因为当数据集包含百万条对话时,整体读取一个 JSON 文件会撑爆内存。
行业标准是使用 .jsonl(JSON Lines)格式:每一行都是一个独立的、合法的 JSON 字典对象。

1. 读取 JSONL 数据集

思路:利用标准的 open() 逐行遍历文件,然后对取出的每一行字符串使用 json.loads() 进行解析。

python 复制代码
dataset = []

# 逐行读取 JSONL 文件
with open("sft_data.jsonl", "r", encoding="utf-8") as f:
    for line in f:
        # 去除行末换行符,并解析该行为字典
        record = json.loads(line.strip())
        dataset.append(record)

print(f"总共加载了 {len(dataset)} 条训练数据!")

2. 写入 JSONL 数据集

思路:遍历 Python 列表中的每一个字典,用 json.dumps() 转化为单行字符串后写入,并手动加上换行符 \n

python 复制代码
results = [
    {"prompt": "1+1=?", "response": "2"},
    {"prompt": "Python的作者是谁?", "response": "Guido van Rossum"}
]

# 追加写入 JSONL 文件 (这里演示用 'a' 模式追加)
with open("output_results.jsonl", "a", encoding="utf-8") as f:
    for item in results:
        # 注意:写入 JSONL 时,indent 必须保持默认的 None,确保每个对象严格挤在一行!
        json_string = json.dumps(item, ensure_ascii=False)
        f.write(json_string + "\n")

六. 高频踩坑避雷指南 (TypeError 报错)

在科研中,你会经常把模型的输出(Tensor)或 NumPy 数组保存到 JSON 里,此时极易遇到以下报错:
TypeError: Object of type ndarray / Tensor / float32 is not JSON serializable

原因: json 库非常基础,它只认识 Python 原生的 dict, list, int, float, str 等。它完全不认识 NumPy 数组或 PyTorch 的张量!

终极解决方案:在传给 json 之前,强制降维降级为 Python 原生类型。

python 复制代码
import numpy as np
import torch

vector_np = np.array([1.1, 2.2, 3.3])
tensor_pt = torch.tensor([4.4, 5.5])

# 错误示范:
# json.dumps({"data": vector_np})  # 报错!

# 正确处理:
# 1. 标量类型使用 .item() 转化为原生 float/int
# 2. 数组/张量使用 .tolist() 转化为原生嵌套 list
safe_data = {
    "np_vector": vector_np.tolist(),
    "pt_tensor": tensor_pt.tolist(),
    "loss_value": tensor_pt[0].item() 
}

# 此时可以完美保存
json.dumps(safe_data)
相关推荐
yejqvow122 小时前
CSS如何控制placeholder文字的颜色_使用--placeholder伪元素
jvm·数据库·python
handler012 小时前
从源码到二进制:深度拆解 Linux 下 C 程序的编译与链接全流程
linux·c语言·开发语言·c++·笔记·学习
m0_743623922 小时前
HTML怎么创建多语言切换器_HTML语言选择下拉结构【指南】
jvm·数据库·python
pele2 小时前
Angular 表单中基于下拉选择动态启用字段必填校验的完整实现
jvm·数据库·python
HHHHH1010HHHHH2 小时前
Redis怎样判断节点是否主观下线_哨兵基于down-after-milliseconds参数的心跳超时判定
jvm·数据库·python
小白学大数据2 小时前
现代Python爬虫开发范式:基于Asyncio的高可用架构实战
开发语言·爬虫·python·架构
渔舟小调2 小时前
P19 | 前端加密通信层 pikachuNetwork.js 完整实现
开发语言·前端·javascript
不爱吃炸鸡柳2 小时前
数据结构精讲:树 → 二叉树 → 堆 从入门到实战
开发语言·数据结构
网络安全许木2 小时前
自学渗透测试第21天(基础命令复盘与DVWA熟悉)
开发语言·网络安全·渗透测试·php