版本比较
标识
- 该行被添加。
- 该行被删除。
- 格式已经改变。
前言
随着 Hugging Face 的爆火,平台上出现了越来越多的有趣的模型和数据集,目前仅模型数量就高达 4 万 5 千多个。
这些模型有一个有趣的特点,在云平台上跑的好好的,但是一旦想在本地跑起来就得各种“费劲”折腾,项目关联的 GitHub 中总是能看到用户反馈:这个模型和代码,我本地跑不起来,运行环境和调用代码搞起来太麻烦了。
在日常的工作和学习中,我们也会经常遇到类似上面 Hugging Face 的情况:许多模型在“云上”跑的好好的,但是一到本地就跑不起来了,这或许是因为“操作系统环境、设备 CPU 架构(x86/ ARM)差异”、或许是因为“Python 运行时版本过高或过低”、或许是因为“某个 PIP 安装的软件包版本不对”、“冗长的示例代码中写死了一堆东西”…
用 Docker 容器搭配 Towhee,制作模型的一键运行环境。以腾讯 ARC 实验室开源的 GFPGAN 模型为例。
制作 PyTorch 模型使用的通用 Docker 基础镜像
通过容器解决或避免以下的情况
- 想要避免不同项目之间的环境造成干扰(污染)
- 想要确保项目依赖清晰,任何人都能够在任何设备上复现结果
- 想要复现模型的时间成本更低一些,不喜欢折腾 80% 重复的模型调优之外的工作内容(尤其是环境、基础配置)
考虑到模型可能需要在 x86 和 ARM 两类设备上运行,推荐使用 miniconda3 这个基于 debian 内置了 conda 工具包的基础镜像。
Dockerfile
代码块 | ||
---|---|---|
| ||
FROM continuumio/miniconda3:4.11.0 LABEL maintainer=waringid@gmail.com ARG USE_MIRROR=1 RUN if [ "$USE_MIRROR" = true ] ; then sed -i -e "s/deb.debian.org/mirrors.tuna.tsinghua.edu.cn/" /etc/apt/sources.list && sed -i -e "s/security.debian.org/mirrors.tuna.tsinghua.edu.cn/" /etc/apt/sources.list; fi RUN apt update && \ apt install -y libgl1-mesa-glx && \ apt autoremove -y RUN if [ "$USE_MIRROR" = true ] ; then pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/; fi RUN conda install -y pytorch RUN pip3 install --upgrade torch==1.9.0 torchvision==0.10.0 RUN pip install towhee |
代码块 | ||
---|---|---|
| ||
docker build -t waringid/ |
制作具体模型的应用镜像
下载所需的模型文件
考虑到国内网络下载 Hugging Face 和 GitHub 模型比较慢,还容易出现网络中断。我推荐大家在做应用模型构建的时候,可以考虑提前进行依赖模型的下载,在构建镜像的过程中,将模型放置到合适的目录位置即可。模型下载可以参考 D004-使用 llama.cpp 转换模型程序的模型下载方法,当然也可以单独下载。
在 GFPGAN 项目中一共依赖俩模型文件,一个是 https://github.com/xinntao/facexlib 项目中基于 ResNet50 的人脸检测模型,另一个是用于图片修复的 GFPGAN 对抗网络模型,也就是传统意义上的“主角”。
第一个模型文件 detection_Resnet50_Final.pth ,可以在 https://github.com/xinntao/facexlib/releases/tag/v0.1.0 中获取;第二个模型则需要我们根据自己的设备状况,来做具体选择:
- 如果你需要使用 CPU 来跑模型,可以在 https://github.com/TencentARC/GFPGAN/releases/tag/v0.2.0 中下载 GFPGANCleanv1-NoCE-C2.pth;或者在https://github.com/TencentARC/GFPGAN/releases/tag/v1.3.0中下载 GFPGANv1.3.pth,这类模型可以完成黑白人像图片的处理。
- 如果你可以使用 GPU 来跑模型,可以在 https://github.com/TencentARC/GFPGAN/releases/tag/v0.1.0 中下载 GFPGANv1.pth;或者在 https://share.weiyun.com/ShYoCCoc 中进行模型文件下载,这类模型可以处理带颜色的人像图片。
- 除了 GitHub 之外,我们也可以选择直接从 Hugging Face 下载模型(只是可选版本不像上面那么多):https://huggingface.co/TencentARC/GFPGANv1/tree/main
python 编写模型调用程序
在 GFPGAN 项目中可以找到官方的模型使用示例:https://github.com/TencentARC/GFPGAN/blob/master/inference_gfpgan.py。
app.py
代码块 | ||
---|---|---|
| ||
from gfpgan import osGFPGANer import cv2 import gradio as gr import torch from basicsr.archs.srvgg_arch import SRVGGNetCompact from basicsr.archs.rrdbnet_arch import RRDBNet from gfpgan.utils import GFPGANer from realesrgan.utils import RealESRGANer def set_realesrgan(version): half=True if torch.cuda.is_available() else False if version == 'v3':towhee @towhee.register class GFPGANerOp: def __init__(self, model_path='/GFPGAN.pth', upscale=2, model = SRVGGNetCompact(num_in_ch=3, num_out_ch=3, num_feat=64, num_conv=32, upscale=4, act_type='prelu') arch='clean', return RealESRGANer(scale=4, model_path='model/realesr-general-x4v3.pth', model=model, tile=0, tile_pad=10, pre_pad=0, half=half) else channel_multiplier=2, bg_upsampler=None) -> None: modelself._restorer = RRDBNetGFPGANer(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=2)model_path, upscale, arch, channel_multiplier, bg_upsampler) def __call__(self, img): return RealESRGANer(scale=2, model_path='model/RealESRGAN_x2plus.pth', model=model, tile=400, tile_pad=10, pre_pad=0, half=half) def set_face_enhancer(upsampler, version): if version == 'RestoreFormer':cropped_faces, restored_faces, restored_img = self._restorer.enhance( img, has_aligned=False, only_center_face=False, paste_back=True) return restored_faces[0][:, :, ::-1] ( towhee.glob['path']('*.jpg') return GFPGANer(model_path='model/RestoreFormer.pth', upscale=2, arch='RestoreFormer', channel_multiplier=2, bg_upsampler=upsampler) else:.image_load['path', 'img']() if version == 'CPU':.GFPGANerOp['img','face']() model_path='model/GFPGANCleanv1-NoCE-C2.pth' elif version == 'v1': arch = 'original' channel_multiplier = 1 model_path='model/GFPGANv1.pth' elif version == 'v1.2': .show(formatter=dict(img='image', face='image')) |
Dockerfile
将下载好的模型文件和新的 Dockerfile 文件放置于相同目录之后,通过整合 Dockerfile 的内容,完成项目依赖的安装,并将模型放置在容器内合适的目录位置。
代码块 | ||
---|---|---|
| ||
FROM waringid/docker-pytorch-playground RUN pip install gfpgan==1.3.8 realesrgan==0.3.0 facexlib==0.3.0 gradio==3.39.0 COPY detection_Resnet50_Final.pth /opt/conda/lib/python3.9/site-packages/facexlib/weights/detection_Resnet50_Final.pth # 尺寸大一些的模型文件,可以选择使用挂载的方式 # 而不在此处直接 COPY 到容器内部 COPY GFPGANCleanv1-NoCE-C2.pth /GFPGAN.pth COPY app.py /entrypoint.py WORKDIR /data RUN pip install IPython pandas RUN sed -i -e "s/display(HTML(table))/with open('result.html', 'w') as file:\n arch = 'clean' file.write(HTML(table).data)/" /opt/conda/lib/python3.9/site-packages/towhee/datacollection/mixins/display.py CMD ["python3", "/entrypoint.py"] |
代码块 | ||
---|---|---|
| ||
docker build -t pytorch-playground-gfpgan -f Dockerfile . |
Image Added
Image Added
模型应用镜像的使用
如果上一步已经下载了模型文件,并将模型文件打包到了镜像中,那么我们只需要下载一些黑白或者彩色的包含人像的图片(根据模型来选择),将它们放在一个目录中(比如 data目录),然后执行一行命令就能够完成模型的调用啦
代码块 | ||
---|---|---|
| ||
docker run --rm -it -v `pwd`/model/GFPGANCleanv1-NoCE-C2.pth:/GFPGAN.pth -v `pwd`/data:/data pytorch-playground-gfpgan |
如果在上文构建应用模型镜像时,没有选择将 GFPGAN 模型打包到镜像中,那么我们就需要使用文件挂载的方式,来运行模型了。为了项目结构的清晰,可以在项目中创建 model 的目录,来存放上文中提到的模型文件。请忽略 Dockerfile 和 app.py 这几个文件。
Image Added
Image Added
在线版的图像模型应用
前面的内容是最简单的模型集成方式,以下的内容通过提供在线访问的方式实现不同的模型和参数的调用,能够在 web 页面上传需要优化的图片并且能够在线展示模型优化后的文件。
先按以下的目录格式下载保存相关的模型(模型文件比较大,提前准备。可以直接下载网盘的资源 https://pan.baidu.com/s/1j6JyPEpCOM4D9NYUnPh0SQ?pwd=soul)
Image Added
app.py
代码块 | ||
---|---|---|
| ||
import os import cv2 import gradio as gr import torch from basicsr.archs.srvgg_arch import SRVGGNetCompact from basicsr.archs.rrdbnet_arch import RRDBNet from gfpgan.utils import GFPGANer from realesrgan.utils import RealESRGANer def set_realesrgan(version): half=True if torch.cuda.is_available() else False if version == 'v3': model = SRVGGNetCompact(num_in_ch=3, num_out_ch=3, num_feat=64, num_conv=32, upscale=4, act_type='prelu') return RealESRGANer(scale=4, model_path='model/realesr-general-x4v3.pth', model=model, tile=0, tile_pad=10, pre_pad=0, half=half) else: model = RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=2) channel_multiplier = 2 model_path='model/GFPGANv1.2.pth' elif version == 'v1.3': return arch = 'clean' RealESRGANer(scale=2, model_path='model/RealESRGAN_x2plus.pth', model=model, tile=400, tile_pad=10, pre_pad=0, half=half) def set_face_enhancer(upsampler, version): if channel_multiplierversion == 2'RestoreFormer': return GFPGANer(model_path='model/GFPGANv1.3RestoreFormer.pth', upscale=2, arch='RestoreFormer', channel_multiplier=2, bg_upsampler=upsampler) else: elifif version == 'v1.4CPU': arch model_path= 'clean'model/GFPGANCleanv1-NoCE-C2.pth' elif channel_multiplier = 2 version == 'v1': arch model_path='model/GFPGANv1.4.pth' 'original' return GFPGANer(model_path=model_path, upscale=2, arch=arch, channel_multiplier=channel_multiplier, bg_upsampler=upsampler) os.makedirs('output', exist_ok=True) def inference(img, realesrgan_version, gfpgan_version, scale): if scale > 4: = 1 model_path='model/GFPGANv1.pth' elif version == 'v1.2': scale = 4 arch = 'clean' elif scale < 0: scalechannel_multiplier = 1 2 try: extension = os.path.splitext(os.path.basename(str(img)))[1]model_path='model/GFPGANv1.2.pth' imgelif version == cv2.imread(img, cv2.IMREAD_UNCHANGED) 'v1.3': if len(img.shape) == 3 and img.shape[2] == 4: arch = 'clean' channel_multiplier = 2 imgmodel_mode path= 'RGBA'model/GFPGANv1.3.pth' elif len(img.shape)version == 2: # for gray inputs'v1.4': img_modearch = None'clean' imgchannel_multiplier = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) 2 else: model_path='model/GFPGANv1.4.pth' return GFPGANer(model_path=model_path, upscale=2, arch=arch, channel_multiplier=channel_multiplier, img_mode = None h, w = img.shape[0:2] bg_upsampler=upsampler) os.makedirs('output', exist_ok=True) def inference(img, realesrgan_version, gfpgan_version, scale): if hscale > 3500 or w > 3500: 4: scale = 4 elif print('too large size')scale < 0: scale = 1 return None, Nonetry: extension = os.path.splitext(os.path.basename(str(img)))[1] ifimg h < 300:= cv2.imread(img, cv2.IMREAD_UNCHANGED) if len(img.shape) == 3 and img.shape[2] == cv2.resize(img, (w * 2, h * 2), interpolation=cv2.INTER_LANCZOS4) 4: upsamplerimg_mode = set_realesrgan(realesrgan_version)'RGBA' face_enhancer = set_face_enhancer(upsampler, gfpgan_version) elif len(img.shape) == 2: # for gray inputs try: img_mode = None _, _, output = face_enhancer.enhance(img, has_aligned=False, only_center_face=False, paste_back=True, weight=None img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) except RuntimeError as errorelse: print('Error', error) img_mode = None h, w = try:img.shape[0:2] if h > 3500 ifor scalew !=> 23500: print('too large size') interpolation = cv2.INTER_AREA if scale < 2 else cv2.INTER_LANCZOS4 return None, None h, w = img.shape[0:2] if h < 300: img output = cv2.resize(outputimg, (int(w * scale /2, h * 2), int(h * scale / 2)), interpolation=interpolation)interpolation=cv2.INTER_LANCZOS4) upsampler = set_realesrgan(realesrgan_version) face_enhancer = set_face_enhancer(upsampler, gfpgan_version) except Exception as errortry: print('wrong scale input.', error) if img_mode == 'RGBA': # RGBA images should be saved in png format_, _, output = face_enhancer.enhance(img, has_aligned=False, only_center_face=False, paste_back=True, weight=None) except RuntimeError as error: extension = 'png'print('Error', error) elsetry: extension = 'jpg' save_path = f'output/out.{extension}'if scale != 2: cv2.imwrite(save_path, output) outputinterpolation = cv2.cvtColor(output, cv2.COLOR_BGR2RGB) return output, save_pathINTER_AREA if scale < 2 else cv2.INTER_LANCZOS4 except Exception as error: print('global exception', error) h, w = img.shape[0:2] return None, None description = r"""Gradio demo for <a href='https://github.com/TencentARC/GFPGAN' target='_blank'>GFPGAN: Towards Real-World Blind Face Restoration with Generative Facial Prior</a>.<br> 这个应用可以被用来修复你的**老照片**或者改善**AI生成的人脸**。<br> 你只需要上传你的图片,就可以使用它。<br> 如果你觉得这个项目帮助到了你,不妨为 <a href='https://github.com/TencentARC/GFPGAN' target='_blank'>它</a> 点个Star吧 :-D <br> """ radio_realesrgan_version = gr.Radio(['v3'], type="value", value='v3', label='RealESR GAN version') radio_gfpgan_version = gr.Radio(['CPU', 'v1.2', 'v1.3', 'v1.4', 'RestoreFormer'], type="value", value='v1.4', label='version') if os.environ.get('BASICSR_JIT') is not None: radio_realesrgan_version = gr.Radio(['v2', 'v3'], type="value", value='v3', label='RealESR GAN version') radio_gfpgan_version = gr.Radio(['CPU', 'v1', 'v1.2', 'v1.3', 'v1.4', 'RestoreFormer'], type="value", value='v1.4', label='version') app = gr.Interface( inference, [ output = cv2.resize(output, (int(w * scale / 2), int(h * scale / 2)), interpolation=interpolation) except Exception as error: print('wrong scale input.', error) if img_mode == 'RGBA': # RGBA images should be saved in png format extension = 'png' else: gr.Image(type="filepath", label="Input"), extension = 'jpg' radio_realesrgan_version, save_path radio_gfpgan_version,= f'output/out.{extension}' grcv2.Number(label="Rescaling factor", value=2),imwrite(save_path, output) ], [ output = gr.Image(type="numpy", label="Output (The whole image)"),cv2.cvtColor(output, cv2.COLOR_BGR2RGB) gr.File(label="Download the output image")return output, save_path ], except Exception as title="GFPGAN: 实用的人脸修复算法",error: description=description, article="<p style='text-align: center'>written by: <a href='https://wiki.waringid.me' target='_blank'></a></p>", # examples=[['AI-generate.jpg', 'v1.4', 2], ['lincoln.jpg', 'v1.4', 2], ['Blake_Lively.jpg', 'v1.4', 2], ['10045.png', 'v1.4', 2]] ) app.queue() app.launch(server_name="0.0.0.0") |
将上面的内容保存为 app.py 后续备用。
简易版本 app.py
以该版本为主
代码块 | ||
---|---|---|
| ||
from gfpgan import GFPGANer import towhee @towhee.register class GFPGANerOp: def __init__(self, model_path='/GFPGAN.pth', upscale=2, arch='clean', print('global exception', error) return None, None description = r"""Gradio demo for <a href='https://github.com/TencentARC/GFPGAN' target='_blank'>GFPGAN: Towards Real-World Blind Face Restoration with Generative Facial Prior</a>.<br> 这个应用可以被用来修复你的**老照片**或者改善**AI生成的人脸**。<br> 你只需要上传你的图片,就可以使用它。<br> 如果你觉得这个项目帮助到了你,不妨为 <a href='https://github.com/TencentARC/GFPGAN' target='_blank'>它</a> 点个Star吧 :-D <br> """ radio_realesrgan_version = gr.Radio(['v3'], type="value", value='v3', label='RealESR GAN version') radio_gfpgan_version = gr.Radio(['CPU', 'v1.2', 'v1.3', 'v1.4', 'RestoreFormer'], type="value", value='v1.4', label='version') if os.environ.get('BASICSR_JIT') is not None: radio_realesrgan_version = gr.Radio(['v2', 'v3'], type="value", value='v3', label='RealESR GAN version') radio_gfpgan_version = gr.Radio(['CPU', 'v1', 'v1.2', 'v1.3', 'v1.4', 'RestoreFormer'], type="value", value='v1.4', label='version') app = gr.Interface( inference, [ channel_multiplier=2gr.Image(type="filepath", label="Input"), radio_realesrgan_version, bg_upsampler=None) -> None:radio_gfpgan_version, self._restorer = GFPGANer(model_path, upscale, arch, channel_multiplier, bg_upsampler) gr.Number(label="Rescaling factor", value=2), def __call__(self], img):[ cropped_faces, restored_faces, restored_img = self._restorer.enhance( gr.Image(type="numpy", label="Output (The whole image)"), img, has_aligned=False, only_center_face=False, paste_back=True) gr.File(label="Download the output image") ], return restored_faces[0][:, :, ::-1] ( towhee.glob['path']('*.jpg') .image_load['path', 'img']() .GFPGANerOp['img','face']() .show(formatter=dict(img='image', face='image')) |
Dockerfile
将下载好的模型文件和新的 Dockerfile 文件放置于相同目录之后,通过整合 Dockerfile 的内容,完成项目依赖的安装,并将模型放置在容器内合适的目录位置。
title="GFPGAN: 实用的人脸修复算法",
description=description,
article="<p style='text-align: center'>written by: <a href='https://wiki.waringid.me' target='_blank'></a></p>", # examples=[['AI-generate.jpg', 'v1.4', 2], ['lincoln.jpg', 'v1.4', 2], ['Blake_Lively.jpg', 'v1.4', 2], ['10045.png', 'v1.4', 2]]
)
app.queue()
app.launch(server_name="0.0.0.0") |
Dockerfile
代码块 | ||
---|---|---|
| ||
FROM nvcr.io/nvidia/pytorch:23.04-py3
LABEL org.opencontainers.image.authors="waringid@gmail.com"
RUN pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/
RUN pip install gfpgan==1.3.8 realesrgan==0.3.0 facexlib==0.3.0 gradio==3.39.0
WORKDIR /app
COPY ./app.py ./
CMD ["python", "app | ||
代码块 | ||
| ||
FROM waringid/docker-pytorch-playground
RUN pip install gfpgan==1.3.8 realesrgan==0.3.0 facexlib==0.3.0 gradio==3.39.0
COPY detection_Resnet50_Final.pth /opt/conda/lib/python3.9/site-packages/facexlib/weights/detection_Resnet50_Final.pth
# 尺寸大一些的模型文件,可以选择使用挂载的方式
# 而不在此处直接 COPY 到容器内部
COPY GFPGANCleanv1-NoCE-C2.pth /GFPGAN.pth
COPY app.py /entrypoint.py
WORKDIR /data
RUN pip install IPython pandas
RUN sed -i -e "s/display(HTML(table))/with open('result.html', 'w') as file:\n file.write(HTML(table).data)/" /opt/conda/lib/python3.9/site-packages/towhee/datacollection/mixins/display.py
CMD ["python3", "/entrypoint.py"] |
代码块 | ||
---|---|---|
| ||
docker build -t pytorch-playgrounddocker-gfpgan . -f docker/Dockerfile . |

模型应用

代码块 |
---|
|
如果上一步已经下载了模型文件,并将模型文件打包到了镜像中,那么我们只需要下载一些黑白或者彩色的包含人像的图片(根据模型来选择),将它们放在一个目录中(比如 data目录),然后执行一行命令就能够完成模型的调用啦
| ||
docker run --gpus all --ipc=host --ulimit memlock=-1 --ulimit stack=67108864 | ||
代码块 | ||
---|---|---|
| ||
docker run --rm -it -v `pwd`/model/GFPGANCleanv1-NoCE-C2.pth:/GFPGAN.pth:/app/model -v `pwd`/datagfpgan:/dataapp/gfpgan pytorch-playground-gfpgan |
如果在上文构建应用模型镜像时,没有选择将 GFPGAN 模型打包到镜像中,那么我们就需要使用文件挂载的方式,来运行模型了。为了项目结构的清晰,可以在项目中创建 model 的目录,来存放上文中提到的模型文件。请忽略 Dockerfile 和 app.py 这几个文件。
Image Removed

-p 7860:7860 docker-gfpgan |
目录 |
---|