精华笔记 – 吴恩达 x Hugging Face《使用Gradio构建生成式AI应用》

Gradio是一个可以让你轻松、方便地使用Python代码构建一个友好Web交互界面的UI框架。它可以支持各种数据类型的输入和输出,还可以方便地与Hugging Face上的各种开源模型集成。

你是否有过开发一个AI应用的想法,并且迫不及待地想要验证其是否可行呢?

你可能已经写好了AI部分的代码逻辑,但是还需要一个能够向他人展示你的创意,并让他们能够亲身体验的用户界面。这样,你才能更有效地验证你的想法,并收集用户的反馈加以改进,从而提升你的系统。

精华笔记 - 吴恩达 x Hugging Face《使用Gradio构建生成式AI应用》

如果你正在寻找这样的解决方案,那么Gradio就是你的理想选择。

Gradio是一个可以让你轻松、方便地使用Python代码构建一个友好Web交互界面的UI框架。它可以支持各种数据类型的输入和输出,还可以方便地与Hugging Face上的各种开源模型集成

在这门课程中,我们将向你展示如何通过Gradio,仅用几行代码搭建一个交互程序,并利用Hugging Face上提供的一些现有模型,完成以下五个有趣的任务:

  • 生成文本摘要:从一篇长文本中抽取出核心信息,生成一个简洁、精确的摘要。
  • 命名实体识别:从文本中识别出人名、地名、组织名等实体,并给它们分类。
  • 识别图像内容:从一张图片中识别出物体、场景、动作等内容,并用自然语言描述出来。
  • 文本生成图像:根据一段描述性的文本,生成一张与之相符合的图片。
  • 搭建基于LLM的聊天机器人:创建一个能够与用户进行自然对话的聊天机器人,并根据用户提供的信息生成个性化的回答。

如果你对这些任务感兴趣,那么请继续阅读本课程,一起探索Gradio和Hugging Face的魅力吧!

NLP任务接口

精华笔记 - 吴恩达 x Hugging Face《使用Gradio构建生成式AI应用》

前面的课程中,我们使用的都是ChatGPT一类的通用大型语言模型,但对于某些特定任务(如生成文本摘要)来说,使用一个专门针对该任务设计的小型专家模型(Small specialist model),也可以表现得与通用大型语言模型一样出色

另一方面,小型专家模型可能还更便宜地运行,以及更快地响应用户

在开始我们的任务之前,我们仍需完成以下两个前置步骤:

加载 API 密钥

python
复制代码
import os # 首次运行,需执行以下指令:pip install python-dotenv from dotenv import load_dotenv, find_dotenv load_dotenv(find_dotenv()) # 读取本地.env文件 hf_api_key = os.environ['HF_API_KEY']

HF_API_KEY 指的是 Hugging Face API 的访问密钥,可以通过以下步骤获取:

  1. 访问 Hugging Face 官网:huggingface.co/
  2. 鼠标移至右上角用户名-点击“Settings”。
  3. 点击“API tokens”选项。
  4. 点击“New token”按钮。
  5. 输入自定义的 API token 名称。
  6. 点击“Create new API token”,以生成一个新的 API token。
  7. 复制 API token 并保存到.env文件。

编写辅助函数

python
复制代码
import requests, json def get_completion(inputs, parameters=None,ENDPOINT_URL=None): headers = { "Authorization": f"Bearer {hf_api_key}", "Content-Type": "application/json" } data = { "inputs": inputs } if parameters is not None: data.update({"parameters": parameters}) response = requests.request("POST", ENDPOINT_URL, headers=headers, data=json.dumps(data) ) return json.loads(response.content.decode("utf-8"))

该辅助函数是用于以API请求的方式访问指定端点进行推理,以完成特定任务的。

Hugging Face 提供了一个Inference API,允许通过简单的 HTTP 请求,免费测试和评估超过 80,000 个可公开访问的机器学习模型或我们自己的私有模型,但有速率限制

ini
复制代码
ENDPOINT_URL = https://api-inference.huggingface.co/models/<MODEL_ID>

<MODEL_ID> 表示我们要运行的模型,可以到 Hugging Face 的模型中心自由挑选适合我们应用业务场景的模型。

精华笔记 - 吴恩达 x Hugging Face《使用Gradio构建生成式AI应用》

除了小型专家模型外,另外一个降低成本并提高速度的方法,就是基于大型模型来训练一个性能非常相似的较小模型,这个过程称为「蒸馏(Distillation)」。

比如我们接下来将使用的 shleifer/distilbart-cnn-12-6 ,就是一个来自于 facebook/bart-large-cnn 的拥有 306M 参数的蒸馏模型。

bart-large-cnn是文本摘要领域最先进的模型之一,由 Facebook 训练而成。

生成文本摘要

在这一部分里,我们将从一篇长文本中抽取出核心信息,生成一个简洁、精确的摘要。

步骤1:定义 summarise 的函数,接受输入,调用 getCompletion 函数,并返回摘要。

python
复制代码
API_URL = "https://api-inference.huggingface.co/models/sshleifer/distilbart-cnn-12-6" def summarize(input): output = get_completion(input, parameters = None, ENDPOINT_URL = API_URL) return output[0]['summary_text']

步骤2:使用 Gradio 的 interface 函数,传入 summarise 函数 ,并将其输入和输出均设置为文本。

ini
复制代码
# 首次运行,需执行以下指令:pip install gradio import gradio as gr demo = gr.Interface(fn=summarize, inputs="text", outputs="text")

步骤3:调用 demo.launch 创建用户界面。

scss
复制代码
demo.launch()

运行结果如下:

精华笔记 - 吴恩达 x Hugging Face《使用Gradio构建生成式AI应用》

(可选)步骤4:进一步优化用户界面,如添加标签、制定行数、增加标题和描述等。

ini
复制代码
demo = gr.Interface(fn=summarize, inputs=[gr.Textbox(label="Text to summarize", lines=6)], outputs=[gr.Textbox(label="Result", lines=3)], title="Text summarization with distilbart-cnn", description="Summarize any text using the `shleifer/distilbart-cnn-12-6` model under the hood!" )

优化结果如下:

精华笔记 - 吴恩达 x Hugging Face《使用Gradio构建生成式AI应用》

命名实体识别

在这一部分里,我们将从文本中识别出人名、地名、组织名等实体,并给它们分类。

精华笔记 - 吴恩达 x Hugging Face《使用Gradio构建生成式AI应用》

这里使用到的是 dslim/bert-base-NER 模型,这是一个针对 NER (Named Entity Recognition,命名实体识别) 任务微调的包含 108M 参数 的 BERT 模型。

BERT 模型是一个用于自然语言处理的机器学习模型,该模型在解析一段文本时,可以识别出文本中包含的人名、组织名、地名等特定实体。

比如,当我们运行以下代码后,它就会输出一个包含多个字典的列表,每个字典都包含一个实体的信息。

ini
复制代码
API_URL = "https://api-inference.huggingface.co/models/dslim/bert-base-NER" text = "My name is Andrew, I'm building DeepLearningAI and I live in California" get_completion(text, parameters=None, ENDPOINT_URL= API_URL)
css
复制代码
[{ 'entity_group': 'PER', 'score': 0.9990624785423279, 'word': 'Andrew', 'start': 11, 'end': 17}, { 'entity_group': 'ORG', 'score': 0.896050214767456, 'word': 'DeepLearningAI', 'start': 32, 'end': 46}, { 'entity_group': 'LOC', 'score': 0.999692440032959, 'word': 'California', 'start': 61, 'end': 71}]

其中,entity_group键表示的含义分别是:

键值含义
PER人物
ORG组织机构
LOC位置

我们可以用 Gradio 让输出更加直观易懂:

步骤1:定义 ner 的函数,接受输入,调用 getCompletion 函数,并返回实体列表。

lua
复制代码
def ner(input): output = get_completion(input, parameters=None, ENDPOINT_URL=API_URL) for entity in output: entity["entity"] = entity['entity_group'] return {"text": input, "entities": output}

步骤2:使用 Gradio 的 interface 函数,传入 ner 函数 ,将输入设置为文本,输出设置为高亮文本。

ini
复制代码
demo = gr.Interface(fn=ner, inputs=[gr.Textbox(label="Text to find entities", lines=2)], outputs=[gr.HighlightedText(label="Text with entities")], title="NER with dslim/bert-base-NER", description="Find entities using the `dslim/bert-base-NER` model under the hood!", allow_flagging="never", examples=["My name is Andrew and I live in California", "My name is Poli and work at HuggingFace"])

HighlightedText 组件的作用是接收并高亮显示NER模型输出的的实体。

examples 参数用于提供示例,帮助用户通快速了解程序是如何工作的。

步骤3:调用 demo.launch 创建用户界面。

scss
复制代码
demo.launch()

运行结果如下:

精华笔记 - 吴恩达 x Hugging Face《使用Gradio构建生成式AI应用》

精华笔记 - 吴恩达 x Hugging Face《使用Gradio构建生成式AI应用》

(可选)步骤4:将被拆分成多个标记的字符重组为一个完整的单词。

同样在前面的课程中我们有介绍过,LLM的处理单元不是一个个「单词」,而是一个个「标记(Token)」。它会接收一系列的字符,并将字符组合在一起,形成代表常见字符序列的标记。每个标记可能对应一个单词,或者空格,或者标点符号。

当我们选用另外一个示例时可以看到,HuggingFace这个单词会被分解为多个块,也即多个标记。

精华笔记 - 吴恩达 x Hugging Face《使用Gradio构建生成式AI应用》

但是,由于我们可以从实体标签的开头字母判断单词的开头和中间部分:

字母含义
B开始标记
I中间标记

因此,我们可以定义一个 merge_tokens 函数,让每个标记在可视化时合并为一个完整单词显示,原理其实就是检查标签的开头字母,并进行合并:

less
复制代码
def merge_tokens(tokens): merged_tokens = [] for token in tokens: if merged_tokens and token['entity'].startswith('I-') and merged_tokens[-1]['entity'].endswith(token['entity'][2:]): # 如果当前 token 延续了上一个 token 的实体,则将它们合并 last_token = merged_tokens[-1] last_token['word'] += token['word'].replace('##', '') last_token['end'] = token['end'] last_token['score'] = (last_token['score'] + token['score']) / 2 else: # 否则,将 token 添加到列表中 merged_tokens.append(token) return merged_tokens

再次运行,就可以看到正确的结果了:

python
复制代码
def ner(input): output = get_completion(input, parameters=None, ENDPOINT_URL=API_URL) merged_tokens = merge_tokens(output) return {"text": input, "entities": merged_tokens}

精华笔记 - 吴恩达 x Hugging Face《使用Gradio构建生成式AI应用》

图像描述应用

在这一部分里,我们将从一张图片中识别出物体、场景、动作等内容,并用自然语言描述出来。

这里使用到的是Salesforce/blip-image-captioning-base模型,这是一个图像描述模型,可以将图像作为输入,并输出该图像的描述。

精华笔记 - 吴恩达 x Hugging Face《使用Gradio构建生成式AI应用》

使用的免费图像来源于: free-images.com/

步骤1:定义 captioner 的函数,接受图像,调用 getCompletion 函数,并返回图像描述。

ini
复制代码
import io import base64 # 将图像转为API所需的Base64格式 def image_to_base64_str(pil_image): byte_arr = io.BytesIO() pil_image.save(byte_arr, format='PNG') byte_arr = byte_arr.getvalue() return str(base64.b64encode(byte_arr).decode('utf-8')) API_URL = "https://api-inference.huggingface.co/models/Salesforce/blip-image-captioning-base" def captioner(image): base64_image = image_to_base64_str(image) result = get_completion(base64_image, parameters=None, ENDPOINT_URL=API_URL) return result[0]['generated_text']

步骤2:使用 Gradio 的 interface 函数,传入 captioner 函数 ,将输入设置为图像,输出设置为文本。

ini
复制代码
demo = gr.Interface(fn=captioner, inputs=[gr.Image(label="Upload image", type="pil")], outputs=[gr.Textbox(label="Caption")], title="Image Captioning with BLIP", description="Caption any image using the BLIP model", allow_flagging="never")

步骤3:调用 demo.launch 创建用户界面。

scss
复制代码
demo.launch()

运行结果如下:

精华笔记 - 吴恩达 x Hugging Face《使用Gradio构建生成式AI应用》

精华笔记 - 吴恩达 x Hugging Face《使用Gradio构建生成式AI应用》

图像生成应用

在这一部分里,我们将根据一段描述性的文本,生成一张与之相符合的图片。

精华笔记 - 吴恩达 x Hugging Face《使用Gradio构建生成式AI应用》

这里使用到的是runwayml/stable-diffusion-v1-5模型,也就是我们所熟知的Stable Diffusion图像生成模型,我们使用API URL连接到了这个模型的服务器。

精华笔记 - 吴恩达 x Hugging Face《使用Gradio构建生成式AI应用》

步骤1:定义 generate 的函数,接受文本描述,调用 getCompletion 函数,并返回图像。

ini
复制代码
# 将PIL图像转换为base64的辅助函数 # 这样你就可以将其发送到API def base64_to_pil(img_base64): base64_decoded = base64.b64decode(img_base64) byte_stream = io.BytesIO(base64_decoded) pil_image = Image.open(byte_stream) return pil_image API_URL = "https://api-inference.huggingface.co/models/Salesforce/blip-image-captioning-base" def generate(prompt): output = get_completion(prompt,parameters=None, ENDPOINT_URL=API_URL) result_image = base64_to_pil(output) return result_image

步骤2:使用 Gradio 的 interface 函数,传入 generate 函数 ,将输入设置为文本,输出设置为图像。

ini
复制代码
demo = gr.Interface(fn=generate, inputs=[gr.Textbox(label="Your prompt")], outputs=[gr.Image(label="Result")], title="Image Generation with Stable Diffusion", description="Generate any image with Stable Diffusion", allow_flagging="never", examples=["a dog in a park","a mecha robot in a favela"])

步骤3:调用 demo.launch 创建用户界面。

scss
复制代码
demo.launch()

运行结果如下:

精华笔记 - 吴恩达 x Hugging Face《使用Gradio构建生成式AI应用》

精华笔记 - 吴恩达 x Hugging Face《使用Gradio构建生成式AI应用》

精华笔记 - 吴恩达 x Hugging Face《使用Gradio构建生成式AI应用》

(可选)步骤4:增加更多输入选项,以构建一个更高级的界面。

ini
复制代码
with gr.Blocks() as demo: gr.Markdown("# Image Generation with Stable Diffusion") with gr.Row(): # gr.Row()用于将组件水平排列 with gr.Column(scale=4): # scale值用于调整所占宽度比例 prompt = gr.Textbox(label="Your prompt") with gr.Column(scale=1, min_width=50): btn = gr.Button("Submit") with gr.Accordion("Advanced options", open=False): # open=false表示默认折叠隐藏 negative_prompt = gr.Textbox(label="Negative prompt") with gr.Row(): with gr.Column(): # gr.Column()用于将组件垂直排列 steps = gr.Slider(label="Inference Steps", minimum=1, maximum=100, value=25, info="In many steps will the denoiser denoise the image?") guidance = gr.Slider(label="Guidance Scale", minimum=1, maximum=20, value=7, info="Controls how much the text prompt influences the result") with gr.Column(): width = gr.Slider(label="Width", minimum=64, maximum=512, step=64, value=512) height = gr.Slider(label="Height", minimum=64, maximum=512, step=64, value=512) output = gr.Image(label="Result") btn.click(fn=generate, inputs=[prompt,negative_prompt,steps,guidance,width,height], outputs=[output])

这些额外输入选项的作用如下:

标签含义类型作用
Your prompt提示文本框描述想要生成的图片内容
Negative prompt负提示文本框描述不想生成的图片内容
Inference Steps推理步骤滑块控件控制推理过程的执行步数,步数越多结果越精细
Guidance Scale指导尺度滑块控件控制文本提示对结果的影响程度,值越大生成结果越贴合提示内容
Width宽度滑块控件设置图片宽度,单位:像素
Height高度滑块控件设置图片高度,单位:像素

运行结果如下:

精华笔记 - 吴恩达 x Hugging Face《使用Gradio构建生成式AI应用》

“描述与生成”游戏

在这一部分里,我们将结合前面两节的内容做一个游戏应用,首先识别一张图像的内容,再将识别出的内容作为描述生成另外一张图像。

步骤1:引入第3课和第4课的函数,captioner 函数用于接受图像并返回图像描述;generate 的函数用于接收图像描述并返回图像。

ini
复制代码
def image_to_base64_str(pil_image): byte_arr = io.BytesIO() pil_image.save(byte_arr, format='PNG') byte_arr = byte_arr.getvalue() return str(base64.b64encode(byte_arr).decode('utf-8')) def base64_to_pil(img_base64): base64_decoded = base64.b64decode(img_base64) byte_stream = io.BytesIO(base64_decoded) pil_image = Image.open(byte_stream) return pil_image def captioner(image): base64_image = image_to_base64_str(image) result = get_completion(base64_image, None, ITT_ENDPOINT) return result[0]['generated_text'] def generate(prompt): output = get_completion(prompt, None, TTI_ENDPOINT) result_image = base64_to_pil(output) return result_image

步骤2:使用 Gradio 的 interface 函数,传入合并了两个步骤的 caption_and_generate 函数 ,将输入设置为图像,输出分别设置为一个文本和一个图像。

ini
复制代码
def caption_and_generate(image): caption = captioner(image) image = generate(caption) return [caption, image] with gr.Blocks() as demo: gr.Markdown("# Describe-and-Generate game 🖍️") image_upload = gr.Image(label="Your first image",type="pil") btn_all = gr.Button("Caption and generate") caption = gr.Textbox(label="Generated caption") image_output = gr.Image(label="Generated Image") btn_all.click(fn=caption_and_generate, inputs=[image_upload], outputs=[caption, image_output]) gr.close_all()

步骤3:调用 demo.launch 创建用户界面。

scss
复制代码
demo.launch()

运行结果如下:

精华笔记 - 吴恩达 x Hugging Face《使用Gradio构建生成式AI应用》

精华笔记 - 吴恩达 x Hugging Face《使用Gradio构建生成式AI应用》

与任意LLM聊天

在这一部分里,我们将创建一个能够与用户进行自然对话的聊天机器人,并根据用户提供的信息生成个性化的回答。

这里使用的到是falcon-40b-instruct模型,这是在Open LLM Leaderboard上排名最高的开源LLM之一。

步骤1:初始化Client,以借助text_generation库访问FalcomLM-instruct推理端点。

python
复制代码
import requests, json from text_generation import Client client = Client(os.environ['HF_API_FALCOM_BASE'], headers={"Authorization": f"Basic {hf_api_key}"}, timeout=120)

步骤2:定义 format_chat_prompt 函数,以格式化提示及对话历史

python
复制代码
def format_chat_prompt(message, chat_history, instruction): prompt = f"System:{instruction}" for turn in chat_history: user_message, bot_message = turn prompt = f"{prompt}nUser: {user_message}nAssistant: {bot_message}" prompt = f"{prompt}nUser: {message}nAssistant:" return prompt

其中涉及到三种角色消息我们也已经介绍过很多次了:

  • 系统消息(System):负责指定LLM整体的语言风格或者助手的行为;
  • 助手消息(Assistant):负责根据用户消息要求内容,以及系统消息的设定,输出一个合适的回应;
  • 用户消息(User):给出一个具体的指令。

而这一步骤的目的是为模型提供记忆功能,使模型能够在理解上下文的前提下,回答后续问题。

vbnet
复制代码
User: what is the meaning of life? Assistant: User: what is the meaning of life? Assistant: I'm sorry, I cannot provide a definitive answer to that question.It is a philosophical question User: but why? Assistant:

步骤3:定义 respond 函数,以调用 Client 的 generate 函数,将消息发送给模型,并将响应追加到对话历史。

ini
复制代码
def respond(message, chat_history): formatted_prompt = format_chat_prompt(message, chat_history) bot_message = client.generate(formatted_prompt, max_new_tokens=1024, stop_sequences=["nUser:", "<|endoftext|>"]).generated_text chat_history.append((message, bot_message)) return "", chat_history

这里有一个问题。

随着这个过程的持续进行,我们发送给模型的对话历史会越来越多,直到模型达到一次对话中可以处理的最大标记数限制。

因此,我们选择在这里将最大标记数(max_new_tokens)参数设置为1024,这将保留最后1024个标记的对话历史。

要想详细这个参数的特点,可以参考之前《精华笔记:吴恩达 x LangChain《基于LangChain的大语言模型应用开发》(上) 》一文中的ConversationalTokenBufferMemory

而另外一个停止序列(stop_sequences)参数的作用,则是为了预防助手消息冒认用户消息,确保对话历史中的用户消息来自于真正的用户而不是来自于模型。

比如,当我们只向模型提问了这一句时:

精华笔记 - 吴恩达 x Hugging Face《使用Gradio构建生成式AI应用》

模型可能会在回答的同时,构造多了一个虚假的提问:

精华笔记 - 吴恩达 x Hugging Face《使用Gradio构建生成式AI应用》

步骤4:使用 Gradio 的 Chatbot 函数,快速构建一个聊天机器人组件

ini
复制代码
with gr.Blocks() as demo: chatbot = gr.Chatbot(height=240) msg = gr.Textbox(label="Prompt") with gr.Accordion(label="Advanced options",open=False): system = gr.Textbox(label="System message", lines=2, value="A conversation between a user and an LLM-based AI assistant. The assistant gives helpful and honest answers.") temperature = gr.Slider(label="temperature", minimum=0.1, maximum=1, value=0.7, step=0.1) btn = gr.Button("Submit") clear = gr.ClearButton(components=[msg, chatbot], value="Clear console") btn.click(respond, inputs=[msg, chatbot, system], outputs=[msg, chatbot]) msg.submit(respond, inputs=[msg, chatbot, system], outputs=[msg, chatbot]) #Press enter to submit

聊天机器人组件可以简化我们发送对话历史给模型的过程。

其中提供的一些额外输入选项的作用如下:

标签含义类型作用
System message系统消息文本框告诉LLM如何与你交流,比如设定角色或调整语气
temperature温度滑块控件为0时,模型对于相同输入会始终做出相同回答;值越大,模型给出的回答变化越大。

(可选)步骤5:让模型改用实时流式的方式传输答案

在这种方式下,我们会将标记逐个发送,并实时地看到它的完整回答,因此不需要等待整个答案准备好,最终呈现的效果就是与ChatGPT一样逐词输出。

ini
复制代码
def respond(message, chat_history, instruction, temperature=0.7): prompt = format_chat_prompt(message, chat_history, instruction) chat_history = chat_history + [[message, ""]] stream = client.generate_stream(prompt, max_new_tokens=1024, stop_sequences=["nUser:", "<|endoftext|>"], temperature=temperature) #stop_sequences to not generate the user answer acc_text = "" #Streaming the tokens for idx, response in enumerate(stream): text_token = response.token.text if response.details: return if idx == 0 and text_token.startswith(" "): text_token = text_token[1:] acc_text += text_token last_turn = list(chat_history.pop(-1)) last_turn[-1] += acc_text chat_history = chat_history + [last_turn] yield "", chat_history acc_text = ""

参考

Inference API-(huggingface.co/docs/api-in…)

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

给TA打赏
共{{data.count}}人
人已打赏
人工智能

薅羊毛!Dify升级并可领400万token额度;YC 23夏季营创业团队清单;开源版妙鸭及原理揭秘;清华大模型课程 | ShowMeAI日报

2024-5-2 3:34:01

人工智能

精华笔记:吴恩达 x LangChain 《使用LangChain构建与数据对话的聊天机器人》(下)

2024-5-2 7:36:53

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