进阶能力MemArts
MemArts介绍
当前ModelArts的常规训练流程为调用MoXing从OBS下载数据到本地SSD后读取本地SSD数据进行训练。然而,这一流程存在以下显著问题:
- 下载时间长:某region的小文件下载带宽通常在70Mbps左右,以2T数据为例,下载时间可能长达8小时,严重影响训练体验。
- 磁盘空间不足:华为云某region的ModelArts训练服务器搭载的SSD大小为4.7T,当数据量超过4.7T时,训练服务器将无法存储全部数据。
- 带宽瓶颈:多个训练节点同时下载数据时,受限于OBS带宽瓶颈,下载时间存在不确定性。
- 高阶特性受限:ModelArts开发了混部、弹性、容错等高阶特性,但由于数据下载时间长,训练过程中拓扑发生变化时重新下载数据的成本极高,严重影响高阶特性的使用体验。
为了解决上述问题,ModelArts提供了MemArts近计算缓存,为用户提供训练服务节点上的数据缓存能力。该方案通过将多块SSD通过网络组成一个分布式数据缓存池,用于缓存训练数据集。在缓存首次装载训练数据时,需要访问OBS将数据下载至缓存中。一旦缓存完成,后续训练数据下载或读取都直接读取MemArts缓存池来完成,从而显著提升训练效率并优化用户体验。
如还需要其他操作,则可以参考其他环境变量配置介绍、使用案例的内容介绍。
约束限制
- 此进阶功能是受限使用阶段,使用前需要联系技术支持,协助专属资源池安装MemArts后方可使用。
- 相关操作需要提前配置环境变量USE_MEMARTS=1并使用MoXing提供的数据API进行数据下载或者读取。
- 设置环境变量后import moxing,设置的环境变量才会生效。
使用方式
借助MemArts能加速数据读取,基础功能使用参照如下介绍。
- copy
#拷贝文件到本地 import moxing as mox mox.file.copy('obs://bucket_name/obs_file.txt', '/cache/obs_file.txt') - copy_parallel
#并发拷贝目录到本地 import moxing as mox mox.file.copy_parallel('obs://bucket_name/sub_dir_0', '/cache/sub_dir_0') - read
# 读取以文本方式读取文件,返回string import moxing as mox file_str = mox.file.read('obs://bucket_name/obs_file.txt') #读取以二进制方式读取文件,返回bytes import moxing as mox file_data = mox.file.read('obs://bucket_name/aaa.jpg',binary=True) - read_meta_free
# 用于超大规模数据读取场景,无需提前做元数据缓存 # 读取以文本方式读取文件,返回string import moxing as mox file_str = mox.file.read_meta_free('obs://bucket_name/obs_file.txt') #读取以二进制方式读取文件,返回bytes import moxing as mox file_data = mox.file.read_meta_free('obs://bucket_name/aaa.jpg',binary=True) #读取write_memarts函数写入MemArts的文件,返回bytes import moxing as mox file_data = mox.file.read_meta_free('obs://bucket_name/obs_file.txt',memarts_only=True) - File
#使用文件对象以文本方式读取文件 import moxing as mox with mox.file.File('obs://bucket_name/obs_file.txt', 'r') as f: file_str = f.read() # 使用文件对象以二进制方式读取文件 import moxing as mox with mox.file.File('obs://bucket_name/obs_file.bin', 'rb') as f: file_bytes = f.read() - write_memarts
#将文件写入MemArts,文件内容需要转换为二进制,返回写入结果。成功为True,失败为False import moxing as mox ret = mox.file.write_memarts('obs://bucket/dir/data.bin', b'xxx', retry=3)
MemArts代理功能
安装2.3.8及以上版本MoXing后,在执行MoXing相关脚本前执行shell命令:
# stop用于故障快恢场景清理原有代理客户端残留 moxing stop_memarts_proxy # 后台开启8个代理客户端保证性能最优 moxing start_memarts_proxy 8
环境变量配置USE_MEMARTS=1,USE_MEMARTS_PROXY=1可以开启MemArts代理功能。无需其他改动即可使用该功能,该功能以性能略微降低的代价极大减少了MemArts进程占用的内存。
其他环境变量配置介绍
MemArts使用到的MoXing API相关的环境变量如下,需要注意的是设置环境变量后import moxing,设置的环境变量才会生效。
- MOX_COPY_PARALLEL_THREADS
该环境变量用于设置copy_parallel函数的线程数,默认值为16,使用16进程并发进行该函数涉及到的列举和拷贝操作。
- MOX_FILE_CHUNK_SIZE
该环境变量用于设置read、read_meta_free、copy、copy_parallel、write_memarts函数的分片大小,单位为Bytes。默认为1MB,通常无需修改。moxing会将所需文件切成MOX_FILE_CHUNK_SIZE设置的大小后基于MemArts或OBS获取对应段的数据。
- MOX_OBS_CLIENT_LOG
该环境变量用于设置obs python sdk日志是否打印,默认设置为1开启. 开启后OBS相关日志会打印到/home/ma-user/modelarts/log/obs_log下。
- MOX_AUTO_READ_META_FREE
该环境变量用于设置是否默认使用read_meta_free函数进行读操作,默认不开启,设置为1后开启。开启后当用户调用read函数时实际使用read_meta_free函数进行读操作。
- MOX_MEMARTS_LARGE_FILE_ACC
该环境变量用于设置copy、copy_parallel并从memarts下载数据时启用大文件加速模式,默认不开启,设置为1后开启。开启后基于环境变量MOX_FILE_PARTIAL_MAXIMUM_SIZE(单位为bytes)判断文件是否属于大文件,该值默认为5GB。当下载文件大于等于环境变量MOX_FILE_PARTIAL_MAXIMUM_SIZE时,基于环境变量MOX_FILE_LARGE_FILE_TASK_NUM得到的并发数来开启多进程下载,该并发数默认为8。建议在代理服务模式下使用。
- MOX_OBS_TO_MOUNT_PATH
该环境变量用于开启sfsturbo等挂载服务兼容功能,默认不开启,值为字符串,其中每个映射由=分隔,多个映射由;分隔,映射的前后分别为obs地址与本地地址。开启后用户调用read, read_meta_free, exists函数和File对象时可以直接使用obs路径读取本地挂载目录中的数据。例如用户在训练任务中使用sfsturbo将obs://test/a 目录挂载到本地c目录中,将obs://test/b 目录挂载到本地/cache/b目录中。那么可以设置环境变量MOX_OBS_TO_MOUNT_PATH为"obs://test/a=/cache/a;obs://test/b=/cache/b"。后续使用read, read_meta_free, exists函数时涉及obs://test/a或obs://test/b目录的数据会直接从sfsturbo挂载目录中读取。
- USE_MEMARTS_PROXY
该环境变量用于设置是否使用memarts客户端代理功能, 当环境中已经存在代理客户端或环境变量设置为1后开启。客户端代理功能相关使用方法参考memarts代理功能。
- USE_METADATA_CACHE
该环境变量用于开启本地元数据缓存,默认不开启,设置为1后开启。开启后使用copy,copy_parallel,read函数读取数据所需要的元数据信息将从本地读取,若本地不存在则从OBS读取后保存在本地,用于提升元数据读取性能。
使用案例
对于用户可能使用到的第三方库功能,可采用如下代码代替使用。
json.load(file_path)可用如下代码代替使用:
json.loads(mox.file.read(file_path, binary=False))
np.load(file_path)可用如下代码代替使用:
np.load(io.BytesIO(moxing.file.read_meta_free(file_path,binary=True)))
np.fromfile(file_path, dtype=dtype)可用如下代码代替使用:
np.frombuffer(mox.file.read(file_path,binary=True), dtype=dtype)
torch.load(file_path)可用如下代码代替使用:
with io.BytesIO(mox.file.read(file_path,binary=True)) as f:
pth = torch.load(f)
ndarray = scipy.sparse.load_npz(file_path)可用如下代码代替使用:
with io.BytesIO(mox.file.read(file_path,binary=True)) as f:
ndarray = scipy.sparse.load_npz(f)
with open(file_path, 'rb') as f:pickle.load(f)可用如下代码代替使用:
with io.BytesIO(mox.file.read(file_path,binary=True)) as f:
data = pickle.load(f)
Image.open(file_path)可用如下代码代替使用:
Image.open(io.BytesIO(moxing.file.read_meta_free(file_path,binary=True)))
cv2.imread(file_path, imread_type)可用如下代码代替使用:
cv2.imdecode(np.frombuffer(mox.file.read_meta_free(file_path,binary=True), np.uint8), imread_type)
在API改造较为困难的情况下,可以使用moxing.file.copy将数据复制到本地,然后通过本地API进行读取和删除操作。在大多数情况下,这种方法不会遇到性能问题。代码示例如下:
_, ext = os.path.splitext(file_path) with tempfile.NamedTemporaryFile(suffix=ext, dir='/cache', delete=False) as fp: moxing.file.copy(file_path, fp.name) data = np.load(fp.name)