首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

【C#深度学习之路】如何使用C#读取safetensors扩展名的大模型文件

  • 25-02-19 08:21
  • 4618
  • 13029
blog.csdn.net

【C#深度学习之路】如何使用C#读取safetensors扩展名的大模型文件

  • 目录
    • Safetensors文件的结构及读取思路
    • 读取方法
      • 读取Header的长度
      • 读取Header的内容
      • 读取tensor的权重值
    • 总结

本文为原创文章,若需要转载,请注明出处。
原文地址:http://iyenn.com/rec/1661923.html
项目对应的Github地址:https://github.com/SciSharp/GGMLSharp/tree/master
C#深度学习之路专栏地址:https://blog.csdn.net/qq_30270773/category_12829217.html
关注我的Github,可以获取更多资料,请为你感兴趣的项目送上一颗小星星:https://github.com/IntptrMax

目录

safetensors是一种十分常见的大模型权重文件。这种模型文件最初由Hugging Face提出,目前广泛用于各类深度学习场景存储模型的权重。在Python环境下,有相应的包可以直接读取safetensors文件的权重内容,用户无需过多关注该模型文件的结构,几乎可以只靠一行代码实现。但是在C#环境下,还没有广泛使用/通用的safetensors文件的读取工具,这给C#开发者造成了不小的困扰。一些开发者会将safetensors文件转换为onnx再开发,但是这样会需要一步额外转化,而且转化时也没脱离python开发环境,所需要的依赖并不少。

为了解决C#下使用Safetensors文件的问题,故开发了相关功能。本文介绍了如何使用C#直接读取safetensors文件的内容及其各项的权重,以方便在C#环境下使用各种不同的深度学习框架,例如Tochsharp、GGMLSharp等,经过简单的处理就可以直接加载Safetensors文件权重。

Safetensors文件的结构及读取思路

Safetensors文件可以认为是一种binary文件,在C#下可以采用流的方式进行读取。

  1. Safetensors文件的结构可以大致分为头部长度+头部+权重内容。其中头部长度占用8个byte,可以转化成一个int64,以表示头部的总长度。
  2. 头部本身是json结构的,可以使用相关的库进行读取。其中每一个元素都包含了tensor的名称、类型、偏移量、形状;
  3. json结构之后是tensor数据的存储部分,需要借助前面的定义来读取;
  4. 可以建立一个新的类,用于存储读取到的tensor的信息;
  5. 为了能够节省内存/显存,提高tensors结构体的读取速度,可以先只读取tensors的结构,在使用tensor的数据时,才进行读取其值;

读取方法

使用C#对Safetensors文件读取,为了减少内存的使用,提高读取速度,建议使用流的方式。

读取Header的长度

Safetensors文件的开头8个字节标识了Header部分的长度,因此可以直接读取,按int64类型转化成整数,这一部分就是这整个Header的长度。

byte[] headerBlock = new byte[8];
stream.Read(headerBlock, 0, 8);
long headerSize = BitConverter.ToInt64(headerBlock, 0);
  • 1
  • 2
  • 3

读取Header的内容

Header的内容可以看作是一个json文件,里面有名称、数据类型、在文件中的偏移量、形状等信息。建立一个新的类来进行读取和存储。

// Read the header, header file is a json file
byte[] headerBytes = new byte[headerSize];
stream.Read(headerBytes, 0, (int)headerSize);

string header = Encoding.UTF8.GetString(headerBytes);
long bodyPosition = stream.Position;
JToken token = JToken.Parse(header);

List<Tensor> tensors = new List<Tensor>();
foreach (var sub in token.ToObject<Dictionary<string, JToken>>())
{
	Dictionary<string, JToken> value = sub.Value.ToObject<Dictionary<string, JToken>>();
	value.TryGetValue("data_offsets", out JToken offsets);
	value.TryGetValue("dtype", out JToken dtype);
	value.TryGetValue("shape", out JToken shape);

	ulong[] offsetArray = offsets?.ToObject<ulong[]>();
	if (null == offsetArray)
	{
		continue;
	}
	long[] shapeArray = shape.ToObject<long[]>();
	if (shapeArray.Length < 1)
	{
		shapeArray = new long[] { 1 };
	}
	GGmlType ggml_type = GGmlType.GGML_TYPE_F32;
	switch (dtype.ToString())
	{
		case "I8": ggml_type = GGmlType.GGML_TYPE_I8; break;
		case "I16": ggml_type = GGmlType.GGML_TYPE_I16; break;
		case "I32": ggml_type = GGmlType.GGML_TYPE_I32; break;
		case "I64": ggml_type = GGmlType.GGML_TYPE_I64; break;
		case "BF16": ggml_type = GGmlType.GGML_TYPE_BF16; break;
		case "F16": ggml_type = GGmlType.GGML_TYPE_F16; break;
		case "F32": ggml_type = GGmlType.GGML_TYPE_F32; break;
		case "F64": ggml_type = GGmlType.GGML_TYPE_F64; break;
		case "U8":
		case "U16":
		case "U32":
		case "U64":
		case "BOOL":
		case "F8_E4M3":
		case "F8_E5M2": break;
	}

	Tensor tensor = new Tensor
	{
		Name = sub.Key,
		Type = ggml_type,
		Shape = shapeArray.ToList(),
		Offset = offsetArray.ToList(),
		FileName = inputFileName,
		BodyPosition = bodyPosition
	};

	tensors.Add(tensor);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58

其中Tensor类的定义如下:

public class Tensor
{
	public string Name { get; set; }
	public Structs.GGmlType Type { get; set; } = Structs.GGmlType.GGML_TYPE_F16;
	public List<long> Shape { get; set; } = new List<long>();
	public List<ulong> Stride { get; set; } = new List<ulong>();
	public string DataNameInZipFile { get; set; }
	public string FileName { get; set; }
	public List<ulong> Offset { get; set; } = new List<ulong>();
	public long BodyPosition { get; set; }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

因为该代码最初是给C#使用ggml而写,所以tensor的类型使用了ggml中的精度类型,如果有需要可以根据自己的平台修改。

读取tensor的权重值

当获取到tensor的结构后就可以读这一部分了,按照流的方式读取,读取时全部按byte读取。tensor在声明时标识了自己的类型,这会在各个平台计算时自己转化。

private byte[] ReadByteFromFile(string inputFileName, long bodyPosition, long offset, int size)
{
	using (FileStream stream = File.OpenRead(inputFileName))
	{
		stream.Seek(bodyPosition + offset, SeekOrigin.Begin);
		byte[] dest = new byte[size];
		stream.Read(dest, 0, size);
		return dest;
	}
}

public byte[] ReadByteFromFile(Tensor tensor)
{
	string inputFileName = tensor.FileName;
	long bodyPosition = tensor.BodyPosition;
	ulong offset = tensor.Offset[0];
	int size = (int)(tensor.Offset[1] - tensor.Offset[0]);
	return ReadByteFromFile(inputFileName, bodyPosition, (long)offset, size);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

总结

C#读取Safetensors文件并不算困难。只是因为使用C#搞深度学习的人并不多,相关功能实现并不普及。撰写本文是希望能够帮助更多喜欢使用C#开发深度学习项目的爱好者更容易实现自己的项目。
该项目的完整代码可以从C#读取safetensors文件方法下载。

该模块来自我正在开发的GGMLSharp项目,如果喜欢该项目,请在GitHub上送我一颗小星星。

注:本文转载自blog.csdn.net的来瓶霸王防脱发的文章"https://blog.csdn.net/qq_30270773/article/details/140413075"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top