前面的文章都在介绍Chat API相关的能力,本篇文章将介绍Image Model API的简单使用
Spring AI 框架集成的图片大模型
2022年出现的三款文生图的现象级产品,DALL-E、Stable Diffusion、Midjourney。
- OpenAI
- dall-e-3
- dall-e-2
- Auzre OpenAI
- dall-e-3
- dall-e-2
- Stability
- stable-diffusion-v1-6
- ZhiPuAI 清华智普
- cogview-3 ( github.com/THUDM/CogVi… )
OpenAI 与 Auzer OpenAI 使用的图片大模型都是 dall-e 系列的。还有一些其它图片大模型,Spring AI 框架并未支持集成,如:Midjourney 发布的 Midjourney 模型, 百度文心一格等
一探文生图大模型的技术核心
关键词:Diffusion模型、CLIP 模型、热平衡、熵驱动、马尔可夫链。
Diffusion模型
Diffusion模型亦即扩散模型,最早是2015年在《基于非平衡热力学的深度无监督学习》(Deep Unsupervised Learning using Nonequilibrium Thermodynamics)论文中提出的。作者受统计热力学的启发,开发了一种新的生成模型。想法其实很简单:首先向训练数据集中的图像不断加入噪声,使之最终变成一张模糊的图像,这个过程就类似于向水中加入一滴墨水,墨水扩散,水变成淡蓝色,然后教模型学习如何逆转这一过程,将噪声转化为图像。
扩散模型的算法实现分为两个过程:
- 正向扩散过程可以描述为逐渐将
高斯噪声
应用于图像,直到图像变得完全无法识别。整个过程可以描述为正向过程的马尔可夫链
(描述从一个状态到另一个状态的转换的随机过程)。同理我们可以将每一张图片定义为一个状态,那每一张图片是什么样子只跟上一张图片有关 - 逆向扩散过程通过神经网络学习的方式近似计算逆向过程的概率分布。
CLIP 模型
CLIP全称为contrastive language-image pre-training,即基于对比学习的大规模图文预训练模型。CLIP模型不仅有着语义理解的功能,还有将文本信息和图像信息结合,并通过注意力机制进行耦合的功能。
我们看一下 Stability AI 公司开发的 Stability AI 模型,其中就使用了CLIP 模型,首先看下其实现架构;
- 文本编码器:将语义转化为计算机可以处理的语言,
文本编码器就是使用了CLIP模型,对文本进行编码处理
- 图像生成器:将编码后的结果转换为符合语义的图像
CLIP模型就是在从网上收集到的4亿张图片和它们对应的文字描述基础上训练出来的。其训练过程如下;
CLIP模型在Stable Diffusion的文本编码器部分发挥了最核心的作用。 该部分内容参考丁磊·生成式人工智能
Spring AI ImageModel源码分析
类结构
classDiagram
Model <|-- ImageModel
ImageModel <|.. OpenAiImageModel
ImageModel <|.. AzureOpenAiImageModel
ImageModel <|.. StabilityAiImageModel
ImageModel <|.. ZhiPuAiImageModel
Model: +TRes call(TReq request)
ImageModel: +ImageResponse call(ImagePrompt request)
class OpenAiImageModel {
- OpenAiImageOptions defaultOptions;
- final OpenAiImageApi openAiImageApi;
+ ImageResponse call(ImagePrompt imagePrompt)
- ImageResponse convertResponse(response,request)
- OpenAiImageOptions toOpenAiImageOptions(ImageOptions options)
}
class AzureOpenAiImageModel {
- OpenAIClient openAIClient
- AzureOpenAiImageOptions defaultOptions;
+ ImageResponse call(ImagePrompt imagePrompt)
- 省略私有方法
}
class StabilityAiImageModel {
- StabilityAiImageOptions options;
- final StabilityAiApi stabilityAiApi;
+ ImageResponse call(ImagePrompt imagePrompt)
- 省略私有方法
}
class ZhiPuAiImageModel {
- ZhiPuAiImageOptions defaultOptions;
- ZhiPuAiImageApi zhiPuAiImageApi;
+ ImageResponse call(ImagePrompt imagePrompt)
- 省略私有方法
}
对于ImageModel
采用策略模式,定义统一接口ImageResponse call(ImagePrompt imagePrompt)
,各自模型的对接各自实现,对于使用者来说都是一样的,直接使用ImageModel
调用。同时也体现了面向接口编程的思想,但是用户也可以直接使用比如OpenAiImageModel
调用。
底层分析
xxxImageModel | 内部Api (真正干活的人儿) |
---|---|
OpenAiImageModel | OpenAiImageApi |
AzureOpenAiImageModel | OpenAIClient |
StabilityAiImageModel | stabilityAiApi |
ZhiPuAiImageModel | zhiPuAiImageApi |
就挑选其中一个看看其底层是如何实现,那就选一个国产的zhiPuAiImageApi
吧
public class ZhiPuAiImageApi {
// 默认的图片模型, 在下面静态代码块中设置的
public static final String DEFAULT_IMAGE_MODEL;
private final RestClient restClient;
// ----------------- 三个构造方法开始 ---------------------------------------
// 根据zhiPuAiToken 初始化 ZhiPuAiImageApi,采用一个默认地址
public ZhiPuAiImageApi(String zhiPuAiToken) {
this("https://open.bigmodel.cn/api/paas", zhiPuAiToken, RestClient.builder());
}
// 根据baseUrl,zhiPuAiToken,以及自定义的RestClient初始化一个ZhiPuAiImageApi
public ZhiPuAiImageApi(String baseUrl, String zhiPuAiToken, RestClient.Builder restClientBuilder) {
this(baseUrl, zhiPuAiToken, restClientBuilder, RetryUtils.DEFAULT_RESPONSE_ERROR_HANDLER);
}
// 根据baseUrl,zhiPuAiToken,以及自定义的RestClient,自定义返回错误处理初始化一个ZhiPuAiImageApi
public ZhiPuAiImageApi(String baseUrl, String zhiPuAiToken, RestClient.Builder restClientBuilder, ResponseErrorHandler responseErrorHandler) {
this.restClient = restClientBuilder.baseUrl(baseUrl).defaultHeaders(ApiUtils.getJsonContentHeaders(zhiPuAiToken)).defaultStatusHandler(responseErrorHandler).build();
}
// ----------------- 三个构造方法结束 ---------------------------------------
// 真正调用智普大模型的创建图片的API
public ResponseEntity<ZhiPuAiImageResponse> createImage(ZhiPuAiImageRequest zhiPuAiImageRequest) {
Assert.notNull(zhiPuAiImageRequest, "Image request cannot be null.");
Assert.hasLength(zhiPuAiImageRequest.prompt(), "Prompt cannot be empty.");
return ((RestClient.RequestBodySpec)this.restClient.post().uri("/v4/images/generations", new Object[0])).body(zhiPuAiImageRequest).retrieve().toEntity(ZhiPuAiImageResponse.class);
}
// 初始化默认图片模型
static {
DEFAULT_IMAGE_MODEL = ZhiPuAiImageApi.ImageModel.CogView_3.getValue();
}
// 请求对象,提示词,模型,用户
@JsonInclude(Include.NON_NULL)
public static record ZhiPuAiImageRequest(String prompt, String model, String user) {
public ZhiPuAiImageRequest(String prompt, String model) {
this(prompt, model, (String)null);
}
public ZhiPuAiImageRequest(@JsonProperty("prompt") String prompt, @JsonProperty("model") String model, @JsonProperty("user_id") String user) {
this.prompt = prompt;
this.model = model;
this.user = user;
}
@JsonProperty("prompt")
public String prompt() {
return this.prompt;
}
@JsonProperty("model")
public String model() {
return this.model;
}
@JsonProperty("user_id")
public String user() {
return this.user;
}
}
// 生成图片返回对象, 生成图片的时间,图片数据
@JsonInclude(Include.NON_NULL)
public static record ZhiPuAiImageResponse(Long created, List<Data> data) {
public ZhiPuAiImageResponse(@JsonProperty("created") Long created, @JsonProperty("data") List<Data> data) {
this.created = created;
this.data = data;
}
@JsonProperty("created")
public Long created() {
return this.created;
}
@JsonProperty("data")
public List<Data> data() {
return this.data;
}
}
// 支持的图片模型的枚举
public static enum ImageModel {
CogView_3("cogview-3");
private final String value;
private ImageModel(String model) {
this.value = model;
}
public String getValue() {
return this.value;
}
}
// 返回data对象,只有一个url,看样子支持url,
// 与OpenAI不同,OpenAI支持b64_json与url
@JsonInclude(Include.NON_NULL)
public static record Data(String url) {
public Data(@JsonProperty("url") String url) {
this.url = url;
}
@JsonProperty("url")
public String url() {
return this.url;
}
}
}
底层实现还是比较简单的,最终还是使用RestClient
调用,技术不论多么花哨,底层基本上都是最朴实无华的简单技术。
Spring AI 接入OpenAI实现文生图
代码实现
package org.ivy.controller;
import jakarta.annotation.Resource;
import org.springframework.ai.jpg>import org.springframework.ai.image.ImagePrompt;
import org.springframework.ai.image.ImageResponse;
import org.springframework.ai.openai.OpenAiImageModel;
import org.springframework.ai.openai.OpenAiImageOptions;
import org.springframework.ai.openai.api.OpenAiImageApi;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ImageController {
@Resource
private OpenAiImageModel openAiImageModel;
/**
* 根据提示词生成图片,并返回图片的URL
*
* @param prompt 提示词
* @return 图片的URL
*/
@GetMapping("/image")
public String image(String prompt) {
ImageResponse imageResponse = openAiImageModel.call(
new ImagePrompt(prompt, OpenAiImageOptions.builder() // 默认model为 dall-e-3
.withModel(OpenAiImageApi.ImageModel.DALL_E_2.getValue())
.withResponseFormat("url") // url or base
.build()
)
);
Image image = imageResponse.getResult().getOutput();
return String.format("<img src='%s' alt='%s'>", image.getUrl(), prompt);
}
}
本示例使用的dall-e2,如果想生成质量更高的图片,建议使用dall-e3,或者接入 Stability AI 使用stable-diffusion
模型。
测试结果
使用的dall-e-2模型生成,看着生成的小狗还是比较丑的。
OpenAiImageOptions 参数说明
- spring.ai.openai.jpg>
- spring.ai.openai.image.options.model:指定的模型,默认dell-e-3
- spring.ai.openai.image.options.quality:图片生成的质量,
仅dell-e-3模型支持此参数
。- spring.ai.openai.image.options.response_format:图片返回的方式,url和b64_json两种
- spring.ai.openai.image.options.size:图片生成的尺寸,dell-e-2只能是 256×256, 512×512, 1024×1024 ,dell-e-3只能是1024×1024, 1792×1024,1024×1792
- spring.ai.openai.image.options.size_width:图片尺寸的宽,遵循上面size对宽的限制
- spring.ai.openai.image.options.size_height:图片尺寸的高,遵循上面size对高的限制
- spring.ai.openai.image.options.style:生成的图像的样式。必须是生动或自然的。生动使模型倾向于生成超真实和戏剧性的图像。自然会使模型生成更自然、更不真实的图像。
仅dall-e-3 支持此参数
。
这里仅对OpenAI Image 的参数进行说明,其它图片大模型的控制参数可以参考官方文档。
其它文生图大模型接入
可以参考官网,接入模式和OpenAI一样,不同之处在于控制参数略有不同
代码示例
总结
本篇探索了图片大模型底层实现的原理,使用 Diffusion 扩散模型,对于图片描述文本使用了CLIP 模型,通过对模型的了解,所有的思路还是对知识的积累,比如马尔可夫链、热平衡扩散原理、熵驱动等 和对日常生活的细心观察,比如墨水在水中的扩散等。都能够提供一些解决问题的思路。