TensorFlow Hub库:简化AI模型训练,提高效率

释放双眼,带上耳机,听听看~!
了解如何使用TensorFlow Hub库简化AI模型训练过程,提高效率,并解决常见的加载和转换问题。

我正在参加「兔了个兔」创意投稿大赛,详情请看:github.com/tensorflow/…

2.1 TensorFlow Hub库

可以说我对 TensorFlow 很熟,而且是它的铁杆粉丝。铁到我的昵称“TF男孩”的TF指的就是 TensorFlow

TensorFlow 已经很简单和人性化了。简单到几十行代码,就可以实现数字识别的全流程(我都不好意写这个教程)。

但是,它依然不满足于此。代码调用已经够简单了。但是对于训练的样本数据、设备性能这些条件,仍然是限制普通人去涉足的门槛。

于是,TensorFlow 就推出了一个 TensorFlow Hub 来解决上面的问题。你可以利用它训练好的模型和权重,自己再做微调,以此适配成自己的成果。这节省了大量的人力和物力的投入。

Hub 是轮毂的意思

TensorFlow Hub库:简化AI模型训练,提高效率

这让我们很容易就联想到“重复造轮子”这个话题。但是,它又很明确,不是轮子,是轮毂。这说明,它把最硬的部件做好了,你只需要往上放轮胎就行。到这里,我开始感觉,虽然我很讨厌有些人说一句话,又是带中文,又是带英文的。但是,这个 Hub ,很难翻译,还是叫 TensorFlow Hub 更为贴切。

2.2 加载image-stylization模型

如果你打算使用 hub 预训练好的风格化模型,自己不做任何改动的话,效果就像下面这样。这是我搞的一个梵高《星空》风格的兔子:

TensorFlow Hub库:简化AI模型训练,提高效率

而代码其实很简单,也就是下面几行:

# 导入hub库
import tensorflow_hub as hub
# 加载训练好的风格化模型
hub_model = hub.load('https://tfhub.dev/google/magenta/arbitrary-image-stylization-v1-256/2')
# 将content和style传入
stylized_image = hub_model(content_image, style_image)[0]
# 获取风格化后的图片并打印出来
tensor_to_image(stylized_image)

这,看起来很简单。似乎人工智能的工作很容易干。

事实,并非如此

TensorFlow Hub库:简化AI模型训练,提高效率

我建议大家都来学习人工智能,利用成熟的代码或工具,解决生活中遇到的问题。

但是,我不建议你着急转行到人工智能的工作岗位中来。

因为在学习过程中,你会发现,相比于其他语言,人工智能具有更多的限制和基础学科要求。因此,作为使用体验者和制作开发者,会是两个不同的心境。

随着下面的讲解,上面的问题我们会逐个碰到。

首先,上面的 hub.load('https://tfhub.dev/……') 你就加载不下来。而这个地址,正是图像风格化的模型文件。咔,晴天霹雳啊,刚起头就是挫折

其实TensorFlow 是谷歌的开源项目。因此他们很多项目的资源是共享的。你可以替换 tfhub.devstorage.googleapis.com/tfhub-modules 。并且在末尾加上后缀 .tar.gz

下载完成之后,解压文件,然后指定加载路径。其实这一步操作,也是框架的操作。它也是先下载到本地某处,然后从本地加载。

比如,我将 .tar.gz 解压到同级目录下。然后调用 hub.load('image-stylization-v1-256_2') 即可完成 hub 的加载。

这就是我说的限制。

相比较而言,Java 或者 Php 这类情况也会有,但是频率没有这么高。

下面,我们继续。还会有其他惊喜

2.3 输入图片转为tensor格式

hub_model = hub.load(……) 是加载模型。我们是加载了图像风格化的模型。

赋值的名称随便起就行,上面我起的名是 hub_model 。之所以说这句话,是因为我发现有些人感觉改个名字,代码就会运行不起来。其实,变一变,更有利于理解代码。而项目运行不起来,向上帝祈祷不起作用,是需要看报错信息的。

如果完全不更改 hub 预置模型的话,再一行代码就完工了。

这行代码就是 stylized_images = hub_model(content_image, style_image)

这行代码是把内容图片 content_image 和风格参照图片 style_image 传给加载好的模型 hub_model 。模型就会输出风格化后的结果图片stylized_images

哇哦,瞬间感觉自己能卖API了。

TensorFlow Hub库:简化AI模型训练,提高效率

但是,这个图片参数的格式,却并没有那么简单。

前面说了,TensorFlow HubTensorFlow 的轮毂,不是轮子,更不是自动驾驶。它的参数和返回值,都是一个 flow 流的形式。

TensorFlow 中的 flow 是什么?这很像《道德经》里的“道”是什么一样。它们只能在自己的语言体系里能说清楚。

但是在这里,你只需要知道调用一个 tf.constant(……) ,或者其他 tf 开头的函数,就可把一个字符、数组或者结构体,包装成为 tensor flow 的格式。

那么下面,我们就要把图片文件包装成这个格式。

先放代码:

import tensorflow as tf

# 根据路径加载图片,并缩小至512像素,转为tensor
max_dim = 512
img = tf.io.read_file(path_to_img)
img = tf.jpg>3)
img = tf.image.convert_image_dtype(img, tf.float32)
shape = tf.cast(tf.shape(img)[:-1], tf.float32)
long_dim = max(shape)
scale = max_dim / long_dim
new_shape = tf.cast(shape * scale, tf.int32)
img = tf.image.resize(img, new_shape)
img = img[tf.newaxis, :]
tf_img = tf.constant(img)

首先,模型在训练和预测时,是有固定尺寸的。比如,宽高统一是512像素。

然后,对于用户的输入,我们是不能限制的。比如,用户输入一个高度为863像素的图,这时我们不能让用户裁剪好了再上传。应该是用户上传后,我们来处理。

最后,要搞成tensorflow需要的格式。

上面的代码片段,把这三条都搞定了。

read_file 从路径读入文件。然后通过 decode_image 将文件解析成数组。

这时,如果打印img,具体如下:

shape=(434, 650, 3), dtype=uint8
array([[[219, 238, 245],
        ...,
        [219, 238, 245]],
        [[219, 238, 245],
        ...,
        [219, 238, 245]]])

shape=(434, 650, 3) 说明这是一个三维数组,看数据这是一张650×434像素且具有RGB三个通道的图片。其中的array是具体像素的数值,在某个颜色通道内,255表示纯白,0表示纯黑。

接着 convert_image_dtype(img, tf.float32) 把img转成了float形式。

此时,img的信息为:

shape=(434, 650, 3), dtype=float32
array([[[0.8588236, 0.9333334, 0.9607844],
        ...,
        [0.8588236, 0.9333334, 0.9607844]],
        [[0.8588236, 0.9333334, 0.9607844],
        ...,
        [0.8588236, 0.9333334, 0.9607844]]])

为啥要把int转为float呢?初学者往往会有这样的疑问。

因为他们发现,只要是计算,就要求搞成float类型。就算明明是9个分类,也不能用1、2、3、4来表示,也要转为一堆的小数点。

今天这个图片的像素,也是如此,255个色值多好辨认,为什么非要转为看不懂的小数呢?

别拦着我,我今天非要要解释一下。

这并不是算法没事找事,假装高级。其实,这是为了更好地对应到很多基础学科的知识。

比如,我在《详解激活函数》中讲过很多激活函数。激活函数决定算法如何做决策,可以说是算法的指导思想

你看几个就知道了。不管是sigmoid还是tanh,它的值都是以0或者1为边界的。

TensorFlow Hub库:简化AI模型训练,提高效率

也就是说你的模型做数字识别的时候,计算的结果并不是1、2、3、4,而是0到1之间的小数。最后,算法根据概率得出属于哪个分类。哎,你看概率的表示也是0到1之间的数。

除此之外,计算机的二进制也是0或者1。芯片的计算需要精度,整数类型不如小数精确。

各种原因,导致还是浮点型的小数更适合算法的计算。甚至,人工智能的体系中,还具有float64类型,也就是64位的小数。

变为小数之后,后面就是将图片数组做缩放。根据数据的shape,找到最长的边。然后缩放到512像素以内。

这就到了 resize(img, new_shape) 这行代码。

到这一步时,img的数据如下:

shape=(341, 512, 3), dtype=float32
array([[[0.8588236, 0.9333334, 0.9607844],
        ...,
        [0.8588236, 0.9333334, 0.9607844]],
       [[0.8588236, 0.9333334, 0.9607844],
        ...,
        [0.8588236, 0.9333334, 0.9607844]]])

原来的 (434, 650, 3) 图片被重新定义成了 (341, 512, 3) 。依然是3通道的色彩,但是长宽尺寸经过计算,最大已经不超过512像素了。

为什么做缩放?除了模型要求,还要防止用户有可能上传一张1亿像素的图片,这时你的服务器就冒烟了。

(434, 650, 3) 代表的是一张图。但是纵观所有算法模型,不管是 model.fit(train_ds) 训练阶段,还是 model.predict(tf_imgs) 预测阶段。就没有处理单张图片的代码逻辑,全都是批量处理。

它不能处理单张图片的结构,你别说它不人性化,不用跟他杠。兄弟,模型要的只是一个数组结构,它并不关心里面图片的数量。一张图片可以是 ["a.png"] 这种形式。

说到这里,我又忍不住想谈谈关于接口设计的话题了。

我给业务方提供了一个算法接口能力,就是查询一张图上存在的特定目标信息。我也是返回多个结果的结构。尽管样本中只有一个目标。业务方非要返回一个。从长远来讲,谁也不敢保证以后场景中只有一个目标。我必须要如实返回,有一个返回一个,有两个返回两个,你可以只取第一个。但是,结构肯定是要支持多个的。

从成本和风险权衡的角度,从列表中取一条数据的成本,要远小于程序出错或者失灵的风险。但是业务方比较坚持返回一个就行。

后来,他们让我把图片的base64返回值带上 data:image/jpeg;base64, 以便于前端直接展示。那一刻,我就明白了,跟他们较这个真,是我冲动了。

而对于 TensorFlow 的要求,你必须要包装成批量的形式。我认为这很规范。

这句代码 img = img[tf.newaxis, :] 就是将维度上升一层。可以将 1 变为 [1] ,也可以将 [[1],[2]] 变为 [[[1],[2]]]

此时再打印img,它已经变为了如下结构:

shape=(1, 341, 512, 3), dtype=float32
array([[[[0.8588236, 0.9333334, 0.9607844],
        ...,
        [0.8588236, 0.9333334, 0.9607844]],
       [[0.8588236, 0.9333334, 0.9607844],
        ...,
        [0.8588236, 0.9333334, 0.9607844]]]])

shape=(1, 341, 512, 3) 表示有1张512×341的彩图。那么,这个结构它也可以承载100张这样的图,那时就是shape=(100, 341, 512, 3)。这就做到了,以不变应万变。

最后一步的 tf_img = tf.constant(img) ,作用是通过 tf.constant 把图片数据,包装成 TensorFlow 需要的格式。

这个格式,就可以传给hub_model去处理了。

经过 stylized_images = hub_model(tf_img_content, tf_img_style) 这行代码的处理。它会将处理结果放到 stylized_images 中。你马上就可以看到融合结果了。

不过,好像也没有那么简单。这个结果的呈现,实际上是图片到tensor格式的逆向过程。

我们下面就来处理它。

2.4 tensor格式结果转为图片

上一步经过 hub_model 转化,我们获取到了 stylized_images 。这是我们辛苦那么久的产物。你是否会好奇 stylized_images 到底是怎样的结构。

我们来打印一下:

[<tf.Tensor: shape=(1, 320, 512, 3), dtype=float32, numpy=
 array([[[[0.31562978, 0.47748038, 0.7790847 ],
          ...,
          [0.7430198 , 0.733053  , 0.6921962 ]],
         [[0.76158   , 0.6912774 , 0.5468565 ],
          ...,
          [0.69527835, 0.70888966, 0.6492392 ]]]], dtype=float32)>]

厉害了,它是一个 shape=(1, 320, 512, 3) 形状的 tf.Tensor 的数组。

不要和我说这些,我要把它转为图片看结果。

来,先上代码:

import numpy as np
import PIL.Image

tensor = stylized_images[0]
tensor = tensor*255
tensor_arr = np.array(tensor, dtype=np.uint8)
img_arr = tensor_arr[0]
img = PIL.Image.fromarray(img_arr)
img.save(n_path) 

相信有了上面图片转 tensor 的过程,这个反着转化的过程,你很容易就能理解。

  • 第1步:取结果中的第一个 stylized_images[0] ,那是 shape=(1, 320, 512, 3)
  • 第2步:小数转为255色值的整数数组 tensor*255np.array(tensor, dtype=np.uint8)
  • 第3步:取出 shape=(1, 320, 512, 3) 中的那个1,也就是512×320的那张图。
  • 第4步:通过 fromarray(img_arr) 加载图片的数组数据,保存为图片文件。

我敢保证,后面的事情,你只管享受就好了

源码在这里 github.com/hlwgy/image… 。你可以亲自运行试验下效果。

不过,多数人还是会选择看完文章再试。

三、一切皆可兔图的效果

春节就要到了,新的一年是兔年(抱歉,我好像说过了)。

下面,我就把小兔子画面和一些春节元素,做一个风格融合。

3.1 年画兔

当然,我只说我这个年龄段的春节场景。

年画,过年是必须贴的,在我老家(倒装句暴露了家乡)。而且年画种类很丰富。

有这样的:

TensorFlow Hub库:简化AI模型训练,提高效率

还有这样的:

TensorFlow Hub库:简化AI模型训练,提高效率

它们的制作工艺不同,作用不同,贴的位置也大不相同。

我最喜欢贴的是门神。老家的门是木头门。搞一盆浆糊,拿扫帚往门上抹。然后把年画一放,就粘上了。纸的质量不是很好,浆糊又是湿的,浆糊融合着彩纸还会把染料扩散开来。估计现在的孩子很少再见到了。

TensorFlow Hub库:简化AI模型训练,提高效率

我们看一下,可爱的小兔子遇到门神年画,会发生怎样的反应:

TensorFlow Hub库:简化AI模型训练,提高效率

你们知道年画是怎么制作的吗?在没有印刷机的年代,年画的制作完全靠手工。

需要先雕刻模子,其作用类似于印章。有用木头雕刻的模具,印出来的就是木板年画。

TensorFlow Hub库:简化AI模型训练,提高效率

好了,雕刻完了。最终的模子是这样的。

TensorFlow Hub库:简化AI模型训练,提高效率

模板里放上不同的染料,然后印在纸上,年画就出来了。

如果兔子遇到这种木板模具,会是什么风格呢?我有点好奇,我们看一下:

TensorFlow Hub库:简化AI模型训练,提高效率

我想,这个图,再结合3D打印机,是不是就不用工匠雕刻了。

3.2 剪纸兔

剪纸,也是过春节的一项民俗。

我老家有一种特殊的剪纸的工艺,叫“挂门笺”。当地叫“门吊子”,意思就是吊在门下的旗子。

TensorFlow Hub库:简化AI模型训练,提高效率

其实这个习俗来源于南宋。那时候过年,大户人家都挂丝绸旗帜,以示喜庆。但是普通百姓买不起啊,就改成了彩纸。

跟年画比,这个工艺现在依然活跃,农村大集还有卖的。

TensorFlow Hub库:简化AI模型训练,提高效率

如果小兔子遇到剪纸,会是什么风格呢?揭晓一下效果:

TensorFlow Hub库:简化AI模型训练,提高效率

3.3 烟花兔

说起烟花,就不是哪个年龄段的专利了。现如今,即便是小孩子,也很喜欢看烟花。

TensorFlow Hub库:简化AI模型训练,提高效率

如果兔子遇到烟花,会产生什么样的融合呢?放图揭晓答案:

TensorFlow Hub库:简化AI模型训练,提高效率

确实很美丽。

四、无限遐想

最后,我仍然意犹未尽。

我尝试自己画了个小兔子,和掘金的吉祥物们做了一个融合:

TensorFlow Hub库:简化AI模型训练,提高效率

这……我感觉比较失落。

但是,转念一想,其实作为抽象画也可以,反正大多数人都看不懂。

我又融合了一张,裱起来,打上落款,好像也过得去。

TensorFlow Hub库:简化AI模型训练,提高效率

上面的例子,我们是 hub.loadhttps://tfhub.dev/……image-stylization-v1-256/2 这个模型。

其实,你可以试试 https://tfhub.dev/……image-stylization-v1-256/1 这个模型,它是一个带有发光效果的模型。

TensorFlow Hub库:简化AI模型训练,提高效率

嗨,技术人,不管你是前端还是后端,如果春节没事干,想跨界、想突破,试试这个人工智能的项目吧。搞个小程序,给亲友用一下,也挺好的。

我是掘金@TF男孩,一位讲代码过程中,多少带点人文气息的编程表演艺术家。

本网站的内容主要来自互联网上的各种资源,仅供参考和信息分享之用,不代表本网站拥有相关版权或知识产权。如您认为内容侵犯您的权益,请联系我们,我们将尽快采取行动,包括删除或更正。
AI教程

FasterTransformer框架介绍及优化

2023-12-11 17:55:14

AI教程

SpringBoot结合ChatGPT对话效果分享

2023-12-11 18:09:14

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索