最近想添加封面图,想尝试使用StableDiffusion的AIGC自动生成封面图。但是,国内chatGPT很容易被封,像DALL·E这种又没法注册,国内百度的文言一格等未开放API,所以只有使用开源大模型了。
类似github管理代码仓库一样,比较有名的管理开源大模型的网站主要有2个:
考虑到第一次使用,学习曲线需要比较平缓才好入门,我使用国内的ModelScope来做大模型HelloWorld。最终决定使用阿里达摩院提供的中文StableDiffusion-通用领域这个大模型进行生成。
M2芯片本地安装环境
官方提供了一个环境配置教程,但是没有看到任何有关M2芯片的提示,于是我转而采用了CSDN上的教程Mac M1 安装 modelscope 深度学习库避免踩坑。
安装ANACONDA
anaconda有点像是python界的docker,可以安装很多个版本的python环境不打架。
- 安装文档:Installation
- 直接下载:Download

创建modelscope python环境
安装完成后可能还不能正常使用conda命令,需要先source一下:
sh
source {安装的路径}/anaconda3/bin/activate然后就可以创建modelscope自己的python环境了,使用3.8版本:
sh
conda create -n modelscope python=3.8
conda activate modelscope官方文档是3.7,但是CSDN上说python3.7和python3.9有问题,所以用3.8。
执行完activate之后,你会发现命令行前面会多一个(modelscope)这样的前缀,说明你进入了modelscope这个环境。
在modelscope环境下,下载Pytorch框架包
可以使用清华源加速:
sh
pip3 install torch torchvision torchaudio -i https://pypi.tuna.tsinghua.edu.cn/simple在modelscope环境下,下载tensorflow框架包
tensorflow似乎就会有mac芯片兼容性问题,因此直接采用CSDN上的解法:
sh
GRPC_PYTHON_BUILD_SYSTEM_OPENSSL=1 GRPC_PYTHON_BUILD_SYSTEM_ZLIB=1 python -m pip install tensorflow-macos -i https://pypi.tuna.tsinghua.edu.cn/simple安装ModelScope常用的包
sh
pip install "modelscope[cv,nlp,multi-modal,science]" -f https://modelscope.oss-cn-beijing.aliyuncs.com/releases/repo.html验证环境正确安装
使用分词模型进行分词:
sh
python -c "from modelscope.pipelines import pipeline;print(pipeline('word-segmentation')('今天天气不错,适合 出去游玩'))"如果环境正确,就能成功对这句话进行分词:
python
{'output': ['今天', '天气', '不错', ',', '适合', '出去', '游玩']}测试大模型
根据中文StableDiffusion-文本生成图像-通用领域,我们可以进行模型测试:
python
import torch
import cv2
from modelscope.pipelines import pipeline
from modelscope.utils.constant import Tasks
task = Tasks.text_to_image_synthesis
model_id = 'damo/multi-modal_chinese_stable_diffusion_v1.0'
pipe = pipeline(task=task, model=model_id, torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32)
output = pipe({'text': '明亮插画 人工智能 chatGPT', 'num_inference_steps': 50, 'guidance_scale': 7.5, 'negative_prompt':'模糊的'})
cv2.imwrite('result.png', output['output_imgs'][0])sh
conda activate modelscope
python test.py期间会下载很久大模型(而且不能中断,中断就得重新来,这点我还没去仔细研究),下载完成后,就会根据你的提示词生成图片,我这里的提示词是明亮插画 人工智能 chatGPT。这里即使没有GPU也可以的,它会使用CPU去跑,生成一张图片大约需要4分钟左右。
然后你就可以得到一张图片: 
这张图默认是512x512像素的,我查了一下,似乎并不能生成非正方形的图像,不过即使是正方形,hexo的cover也能自动调整显示尺寸。另外有一个遗留问题,就是图片比较模糊,暂时没有解决方法。大概率是因为使用了贫穷的CPU进行跑图的关系。
使用ChatGPT帮我在博客里注入cover标签
生成基本逻辑代码
提示
http
POST https://api.openai.com/v1/chat/completions
{
"model": "gpt-3.5-turbo",
"messages": [
{
"role": "user",
"content": "用python3完成如下功能,注意是macOS操作系统:我有一堆hexo博客文件,格式是markdown。如果文件内容头部有类似'cover: /image/123.jpg'的注解,说明有封面图了,跳过;如果文件内容头部没有类似注解,则调用test.py为其生成一张图片到image目录下,并插入注解到内容中"
}
]
}回复
以下是一种实现方式,其中假设markdown文件都存放在post文件夹下,test.py脚本用来生成封面图并返回文件名:
python
import os
import subprocess
# 函数: 判断markdown文件中是否包含封面注解
def has_cover(path):
with open(path, 'r') as f:
lines = f.readlines()
for line in lines:
if line.startswith('cover:'):
return True
return False
# 函数: 为markdown文件生成封面图并返回文件名
def generate_cover(path):
# 调用test.py脚本生成封面图
cmd = f'python3 test.py {path}'
filename = subprocess.check_output(cmd, shell=True).decode('utf-8').strip()
# 将封面注解插入markdown文件中
with open(path, 'r+') as f:
lines = f.readlines()
lines.insert(2, f'cover: /image/{filename}\\n')
f.seek(0)
f.writelines(lines)
return filename
# 遍历所有markdown文件
for filename in os.listdir('post'):
if filename.endswith('.md'):
path = os.path.join('post', filename)
# 判断是否已经有封面注解
if not has_cover(path):
# 生成封面图并插入注解
generate_cover(path)需要注意的是,test.py脚本需要自己编写,用于生成封面图并返回文件名。另外,上述代码中默认在博客根目录下建立image文件夹,用于存放生成的封面图。如果需要修改文件夹名或路径,可以修改代码中对应的部分。
分析
近乎完美,逻辑清晰,语法正确,而且对于不熟悉python的我来说,很多地方我都想不到的,比如:
{filename}字符串竟然可以直接这样注入- subprocess竟然可以运行command命令
- 我想的是完全解析整个文件,但它的思路是
line.startswith('cover:'),以及lines.insert
不过有个缺憾,也怪我没有提示清楚,就是hexo的markdown头部,是用yaml写的,我想利用tags来作为提示词,但是yaml有两种写法:
yaml
## 写法1
tags: [标签1, 标签2]yaml
## 写法2
tags:
- 标签1
- 标签2我的习惯是第1种写法,但是abbr这个hexo插件在注入abbrlink时会自动改为第2种写法,这就导致两种写法都存在,都得解析。如果直接用line.startswith就不太好搞。
解决提取标签的问题
于是我又问了一下chatGPT思路,如何提取标签?
提示
json
{
"model": "gpt-3.5-turbo",
"messages": [
{
"role": "user",
"content": "用python3完成如下功能,注意是macOS操作系统:有一个markdown文件,需要提取tags内容,提取为python数组。其格式可能为'tags:\n- 平行宇宙\n- 量子力学',也可能为'tags:['平行宇宙', '量子力学']'"
}
]
}回复
json
{
"id": "chatcmpl-7Gkdb9UAEWK0sdP8TQGzeYNlEolyn",
"object": "chat.completion",
"created": 1684226879,
"model": "gpt-3.5-turbo-0301",
"usage": {
"prompt_tokens": 84,
"completion_tokens": 367,
"total_tokens": 451
},
"choices": [
{
"message": {
"role": "assistant",
"content": "可以使用正则表达式来提取tags内容,代码如下:\n\n```python\nimport re\n\nwith open('example.md', 'r') as f:\n content = f.read()\n tags_match = re.search(r'^tags:([\\s\\S]*?)(?=^\\w)', content, re.M)\n if tags_match:\n tags_str = tags_match.group(1)\n tags = re.findall(r'[\\w\\s]+', tags_str)\n tags = [tag.strip() for tag in tags]\n print(tags)\n```\n\n这里的正则表达式`r'^tags:([\\s\\S]*?)(?=^\\w)'`的含义是:从文本开头匹配'tags:',然后匹配任意非空白字符和空白字符0到无限次,非贪婪模式,直到遇到一个以字母或数字开头的行为止。\n\n如果查找成功,则会得到一个含有tags内容的字符串,例如' - 平行宇宙\\n - 量子力学\\n'或\"['平行宇宙', '量子力学']\"。接下来,使用正则表达式`r'[\\w\\s]+'`提取出所有的单词和空格,再通过循环去除空格并添加到一个数组中即可。最后将数组打印出来即可。\n\n注:假设markdown文件中只含有一组tags内容。如果含有多组tags内容,只提取第一组。如果不含有tags内容,则不会打印任何东西。"
},
"finish_reason": "stop",
"index": 0
}
]
}分析
正则的思路很好,不过我还是觉得不太可靠,毕竟文本中也可能出现tags:xxx这种被匹配出来,虽然也有方法去除,但可靠性不太高,不想给自己挖坑。
最终脚本
最终,我还是自己采用了yaml解析的方式解决这个问题。不过仍然参考了大量chatGPT提供的代码。
python
import os
import torch
import cv2
from modelscope.pipelines import pipeline
from modelscope.utils.constant import Tasks
import yaml
# 解析博客文件
def parse_blog(blog_md_file):
with open(blog_md_file, 'r', encoding='utf-8') as f:
raw_data = f.read()
pattern = '---'
blog_data = {}
search_result = list(pattern_search(raw_data, pattern))
if len(search_result) >= 2:
blog_info_str = raw_data[search_result[0] + len(pattern): search_result[1]]
blog_data = yaml.load(blog_info_str, Loader=yaml.SafeLoader)
blog_data['content'] = raw_data[search_result[1] + len(pattern):]
return blog_data
# 按指定格式切分内容
def pattern_search(string, pattern):
index = 0
while index < len(string)-len(pattern):
index = string.find(pattern, index, len(string))
if index == -1:
break
yield index
index += len(pattern)-1
# 函数: 为markdown文件生成封面
def generate_cover(path, blog_data):
# 使用stable diffusion生成封面图
task = Tasks.text_to_image_synthesis
model_id = 'damo/multi-modal_chinese_stable_diffusion_v1.0'
pipe = pipeline(task=task, model=model_id, torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32)
text = "明亮插画 " + " ".join(blog_data.get("tags"))
cover_path = '/cover/' + str(blog_data.get("abbrlink")) + ".png"
print("generating cover for " + path + ", prompt is " + text + ", image name is: " + cover_path)
output = pipe({'text': text, 'num_inference_steps': 50, 'guidance_scale': 7.5, 'negative_prompt': '模糊的'})
cv2.imwrite("source" + cover_path, output['output_imgs'][0])
# 将封面注解插入markdown文件中
with open(path, 'r+') as f:
lines = f.readlines()
lines.insert(2, f"cover: {cover_path}\n")
f.seek(0)
f.writelines(lines)
return filename
if __name__ == '__main__':
# 遍历所有markdown文件
for filename in os.listdir('source/_posts'):
if filename.endswith('.md'):
path = os.path.join('source/_posts', filename)
blog_data = parse_blog(path)
# 判断是否已经有封面注解
if not ('cover' in blog_data.keys()):
# 生成封面图并插入注解
filename = generate_cover(path, blog_data)总结
这次尝试让我熟悉了大模型和chatGPT的使用。我之前一直觉得chatGPT不太能辅助写代码,但是这次深刻体会到的一点是,chatGPT不太能辅助你写你擅长语言的熟悉的代码。如果让chatGPT写Java,我能挑出一百个毛病。但是如果让它写python,我会惊叹原来还能这样写、原来还有这个库、原来还有这个函数!它会加速你实现目标的过程,无需担心编程语法。另外,就算是Java,我也不熟悉文件操作之类的日常不会使用的库,相比于网上搜索,直接问chatGPT来得更快,而且更符合需求。



粤公网安备44030602007943号