OPT大语言模型在Unity中简单应用
概述
目前大语言模型非常火热,热度非常高,接下来我将介绍大语言模型在Unity中的一个简单的应用,非常简单,方便上手,欢迎大家复现与讨论!
首先先带大家介绍一下Opt350m大模型:这是huggingface上关于Opt350m大模型的介绍,大家可以去看看链接我放在这里了:https://huggingface.co/facebook/opt-350m。
Opt350m 是一个由 Meta AI(以前称为 Facebook AI)开发的大型语言模型。它是 Open Pretrained Transformer(OPT)系列中的一员,专门用于自然语言处理任务。以下是一些关于 OPT350m 的基本信息:
- 规模与架构:OPT350m 的名称中的“350m”表示该模型具有大约3.5亿个参数。相比于更大的模型(如 GPT-3 具有 1750 亿个参数),OPT350m 的规模较小,但仍然能够处理许多自然语言任务。
- 训练数据:像许多语言模型一样,OPT350m 是通过大量的文本数据进行预训练的。训练数据通常包括互联网文本、书籍、文章等多种来源,以帮助模型理解和生成自然语言。
- 应用场景:由于其较小的规模,OPT350m 在计算资源方面相对友好,适合用于需要较高响应速度和较低计算需求的应用场景。它可以应用于对话生成、文本总结、问答系统等任务。
- 开放性:Meta AI 通常会在其模型发布时提供详细的技术文档和研究论文,以便社区可以了解模型的结构、训练过程和性能。OPT350m 也不例外,Meta AI 会提供相关的信息来帮助研究人员和开发者理解和使用模型。
- 对比与性能:虽然 OPT350m 的参数量比许多更大的模型少,但它仍然能够在许多自然语言处理任务中提供有用的性能。相对较小的模型可能在某些复杂任务中表现不如大型模型,但在速度和资源消耗方面具有优势。
1.大语言模型的前期准备工作
第一步 需要配置好大语言模型的环境,我选择的是目前最小的大语言模型OPT-350M,参数只有350M个,非常小,所以效果没有那么好,只能当作新手去练习,我看到没有新手教程来使简单使用大语言模型,这是一个很好的列子,且对新手非常好的一个例子。
首先你可以从Huggingface或者modelscope上将模型下载下来,OPT350M不大,下载下来比较快,我更推荐modelscope下载,因为很多时候挂梯子也不稳定,所以modelscope是最好的选择,而且modelscope教程非常详细,跟着步骤一步一步来就好。
网址我给放在下面:
https://www.modelscope.cn(教程非常详细,对新手非常友好!)
其次就是你要测试你的大模型时候安装成功,我个人非常推荐的环境是liunx,因为windows会出现很多问题,虚拟机系统会节省很多事情!!!!这个我自己亲身体会,所以说大家尽量选用虚拟机。具体的环境配置我就不带大家来操作了,huggingface上很全面,大家一步一步跟着来就好:
我来教大家怎么测试自己的环境时候安装成成功:
from transformers import AutoModelForCausalLM, AutoTokenizer
tokenizer_path = '(模型存放的路径)'
tokenizer = AutoTokenizer.from_pretrained(tokenizer_path)
model_path = '(模型存放的路径)'
model = AutoModelForCausalLM.from_pretrained(model_path).to('cuda')
# 输入模板,包含问题和答案
input_template = "Question: {question}\nAnswer: {answer}\nPlease write a detailed description:"
question = "what is it ?"
answer = "A cat."
input_text = input_template.format(question=question, answer=answer)
# 编码输入文本
input_ids = tokenizer.encode(input_text, return_tensors="pt").to('cuda')
generated_text = model.generate(
input_ids=input_ids,
max_length=500, # 指定最大长度,根据需要调整
num_return_sequences=1
)
# 解码和输出结果
output_text = tokenizer.decode(generated_text[0], skip_special_tokens=True)
print(output_text)
在终端运行自己的脚本:python your_name.py
运行成功后出现:
达到这种效果就算是成功!!恭喜你已经完成了第一步,也是最关键的第一步!!(长度可以自己设定,我设置的比较长所以生成的文本比较多而且比较乱)。
2.大语言模型的测试与API的调用
接下来,我们要调试服务器的连接性,我们的原理其实就是很简单,调用大语言模型的API,让我们远程来应用,说白了我们就是提出问题让大语言模型来回答,根据回答物体做出相应的动作。首先你可以先写一个python脚本来测试一下服务器是否可以连接,启动 Flask 服务器的线程,这个非常简单,大家可以在csdn上搜索一下,就两行代码,我可以教一下大家怎么写测试脚本,我写了两个python脚本,一个是服务器本身脚本,另一个是client脚本,用来向服务器发送请求来让服务器知道我要干嘛,比如我向服务器发送一个prompt,提示服务器我想让他帮我回答什么问题,下边是服务器的代码:`
from flask import Flask, request, jsonify
from transformers import OPTForCausalLM, AutoTokenizer
import requests
import os
import threading# 定义模型路径
model_path = "模型的路径" # 确保路径格式正确# 确认路径下的文件
def check_model_files(path):
required_files = ["config.json", "merges.txt", "pytorch_model.bin", "special_tokens_map.json", "tokenizer_config.json", "vocab.json"]
missing_files = [file for file in required_files if not os.path.isfile(os.path.join(path, file))]
if missing_files:
print(f"Missing files: {missing_files}")
return not missing_files
# 检查模型文件
if not check_model_files(model_path):
raise FileNotFoundError(f"Model files not found in {model_path}. Please check the path and files.")
# 从本地加载模型和分词器
try:
tokenizer = AutoTokenizer.from_pretrained(model_path, local_files_only=True)
model = OPTForCausalLM.from_pretrained(model_path, local_files_only=True).to("cuda")
except Exception as e:
print(f"Error loading model: {e}")
raise e
# 创建 Flask 应用
app = Flask(__name__)
@app.route('/control', methods=['POST'])
def control_sphere():
try:
data = request.json
print(data)
prompt = data.get('prompt', '')
# 打印接收到的 prompt
print(f"Received prompt: {prompt}")
# 使用 OPT 模型生成文本
inputs = tokenizer(prompt, return_tensors="pt").to("cuda")
outputs = model.generate(**inputs, max_length=200)
generated_text = tokenizer.decode(outputs[0], skip_special_tokens=True)
# 打印生成的文本
print(f"Generated text: {generated_text}")
# 解析生成的文本
direction = "none"
if "left" in generated_text.lower():
direction = "left"
elif "right" in generated_text.lower():
direction = "right"
elif "up" in generated_text.lower():
direction = "up"
elif "down" in generated_text.lower():
direction = "down"
# 打印返回的方向
print(f"Returning direction: {direction}")
return jsonify({"direction": direction})
except Exception as e:
print(f"Error during processing: {e}")
return jsonify({"direction": "none", "error": str(e)}), 500
# 启动 Flask 服务器的线程
def start_flask_server():
app.run(host="0.0.0.0", port=5000)
# 发送指令到服务器并打印响应
def send_command_to_server(prompt):
url = "http://localhost:5000/control"
data = {"prompt": prompt}
try:
response = requests.post(url, json=data)
print(f"服务器响应状态码: {response.status_code}") # 打印响应状态码
print(f"服务器响应内容: {response.text}") # 打印响应内容
return response.json()
except requests.exceptions.RequestException as e:
print(f"Request failed: {e}")
return {"direction": "none", "error": str(e)}
if __name__ == "__main__":
app.run(port=8896)
说白了我们采用POST来连接服务器,一旦服务器接收到请求,就会发出相应的指令,我们又写了个脚本来检验一下服务器是否能正常接收到请求再发送指令:
from transformers import OPTForCausalLM, AutoTokenizer
import requests
import os
import threading
# 发送指令到服务器并打印响应
def send_command_to_server(prompt):
url = "http://localhost:8896/control"
data = {"prompt": prompt}
try:
response = requests.post(url, json=data)
print(f"服务器响应状态码: {response.status_code}") # 打印响应状态码
print(f"服务器响应内容: {response.text}") # 打印响应内容
return response.json()
except requests.exceptions.RequestException as e:
print(f"Request failed: {e}")
return {"direction": "none", "error": str(e)}
# 命令行接口
def command_line_interface():
while True:
prompt = f"we have four directions: up and down, please random pick one."
if prompt.lower() == 'exit':
break
result = send_command_to_server(prompt)
if 'direction' in result:
print(f"服务器返回的方向: {result['direction']}")
if 'error' in result:
print(f"服务器返回的错误: {result['error']}")
break
command_line_interface()
这里也可以自己设置端口号,我的建议是设置在8000以上,绝对好用!!
当你在终端运行脚本时,接收到命令后就可以正常运行,说明服务器的接收和发送请求都没有问题。至此第一部分就已经全部完成,祝贺大家!运行的效果如图所示:
另一个终端就会出现:
这样就是说明服务器可以接收到我们发送的请求,证明我们配置成功,下边我们就是把它应用到Unity中来实现一些简单功能。
3.Unity中的实现
接下来我们我们就要做对Unity的操作了,我们就是做一个非常简单的测试,创建一个物体,随便创建一个即可,无论你想创建什么物体。我们主要是保证简单操作,毕竟只是我们熟悉这个过程!
以我为例,我创建的就是一个球体,我命名为jason(随便起的名字),自己创建一个脚本,名字为MoveSpeed,不用和我一样,随意即可。我们要做的就是把脚本拖拽到创建的物体上。我用的Unity的版本为2022,我的建议是先汉化,用起来比较方便:
接下来就是脚本的调试,我们的主要核心思想是物体先要给大语言模型一个prompt,让大语言模型知道你要求的方向在哪里,要去做什么。大语言模型根据发过来的prompt来随机生成一个文本信息发送回去,物体接收到相应的指令来根据指令移动。原理其实很简单我们下载的大模型在我们自己的服务器上,我们怎么去调用这个大模型就是很关键的步骤!
我自己画了一个原理的图片来帮助大家来理解:
图片配合着文字很好理解!
下一步就是可以实现自己想要的效果的时候,见证奇迹了!
我们先要开始运行自己的脚本,在Vscode中的终端输入python your_name.py,即可运行,运行之后呢大语言模型就要开始发力了!
让我来大家展示一下:
运行python脚本后就是这个情况:
我们要在Unity中点击播放这个功能,让物体开始发送指令,服务器中的大模型正在等待接受一个prompt。点击播放后我们就可以在终端看到这个效果:
因为这个OPT大语言模型参数比较少,所以生成的效果并没有那么好,但是也足够新手拿来来练练手。(-。-)
我们同时可以在Unity中可以看到,物体也根据OPT大语言模型生成的指令来移动,效果如下:
这是我自己录制的视频,大家可以看看效果。
Unity实战操作
到达这一步,我们就全部完成!恭喜你,迈出了勇敢的第一步!接下来就是考验自己的动手能力了,网上有很多的开源的大模型可以自己来动手去调试,慢慢的积累经验你也会成为一名资深的老玩家!
对啦对啦,最后附赠C#脚本调用大语言模型的API:
using System.Collections;
using UnityEngine;
using UnityEngine.Networking;
public class MoveSpeed : MonoBehaviour
{
public float moveSpeed = 50f;
private Vector3 movementDirection = Vector3.zero;
private string apiUrl = "http://localhost:8896/control"; // Flask服务端的URL
private string predefinedPrompt = "We have four directions: up, down, left, and right. Please random pick one,up and down priority"; // 预定义的prompt
private void Start()
{
Debug.Log("Starting MoveSpeed script.");
StartCoroutine(SendRequest(predefinedPrompt));
}
private void update()
{
transform.Translate(movementDirection * moveSpeed * Time.deltaTime);
//Debug.Log("Keep moving");
}
private IEnumerator SendRequest(string prompt)
{
while (true)
{
// 创建一个JSON对象,并将prompt放入其中
string jsonData = "{\"prompt\":\"" + prompt + "\"}";
Debug.Log($"Sending request with data: {jsonData}");
using (UnityWebRequest www = new UnityWebRequest(apiUrl, "POST"))
{
// 配置上传和下载处理器
www.uploadHandler = new UploadHandlerRaw(System.Text.Encoding.UTF8.GetBytes(jsonData));
www.downloadHandler = new DownloadHandlerBuffer();
www.SetRequestHeader("Content-Type", "application/json");
yield return www.SendWebRequest();
if (www.result != UnityWebRequest.Result.Success)
{
Debug.LogError($"Request Error: {www.error}");
yield break;
}
else
{
string jsonResponse = www.downloadHandler.text;
Debug.Log($"Received Response: {jsonResponse}");
CommandResponse response;
try
{
response = JsonUtility.FromJson<CommandResponse>(jsonResponse);
Debug.Log($"Parsed Response Direction: {response.direction}");
HandleCommand(response.direction);
}
catch (System.Exception ex)
{
Debug.LogError($"Error parsing JSON: {ex.Message}");
yield break;
}
}
yield return new WaitForSeconds(1f);
}
}
}
private void HandleCommand(string direction)
{
Debug.Log($"Handling command: {direction}");
Vector3 movementDirection = Vector3.one;
switch (direction.ToLower())
{
case "left":
movementDirection = Vector3.left;
Debug.Log("Moving left");
break;
case "right":
movementDirection = Vector3.right;
Debug.Log("Moving right");
break;
case "up":
movementDirection = Vector3.up;
Debug.Log("Moving up");
break;
case "down":
movementDirection = Vector3.down;
Debug.Log("Moving down");
break;
case "none":
Debug.Log("No movement command received");
break;
default:
Debug.LogWarning($"Unknown direction: {direction}");
break;
}
if (movementDirection != Vector3.zero)
{
GameObject jason = GameObject.Find("jason");
if (jason != null)
{
jason.transform.Translate(movementDirection);
Debug.Log($"Object moved by {movementDirection}");
}
else
{
Debug.LogError("No GameObject named 'jason' found in the scene.");
}
}
else
{
Debug.Log("No movement executed");
}
}
[System.Serializable]
private class CommandResponse
{
public string direction;
}
}
说一下我自己的感受,自己能动手亲自实现才会知道要经历多少困难虽说只是一个简单的应用,会出现很多麻烦和问题,大家在遇到问题时一定要记录下来去查询去解决,也非常感谢我的博士师兄的帮助!
大家跟着我的操作一步一步的来,多数会没有问题的,我已经帮大家都做好了工作,只等大家来亲自动手实践了!下面我也会陆续记录其他能力更强的大模型的调用和实践操作,希望大家多多支持!我也会很快回来的!(预告一下,下一篇的大模型为BLIP2-XL为一个多模态输入的大模型,实现的功能会更强)大家敬请期待!也欢迎大家多多讨论与交流,如果大家不会的地方比较多我会出一期视频来讲解一下!