总结:
代码报错搜了很多知识,然后按照搜到的一个个去尝试能否解决,结果越搞越乱,不如重新梳理代码逻辑和思路,系统地掌握架构,这样能找到报错关键点。
本周知识清单:
- python装饰器
- fastAPI使用
- 利用FunASR和fastapi创建一个语音识别的接口
- 千问1.5代码生成微调
下周任务:
- 千问代码微调复现
- 千问代码微调技术分享
- 千问代码对外提供服务
目录
2.里面的2-2节中的async def 是用于定义异步函数的关键字
3.如果路径参数设置成整数:def read_item(item_id: int):
12.第九节的Body - Nested Models(嵌套模型)
关键记录
python装饰器
参考:【Python】一文弄懂python装饰器(附源码例子)_python 装饰器-CSDN博客
装饰器,就是可以让我们拓展一些原有函数没有的功能。
装饰器的语法糖@
简单的装饰器还是可以通过语法糖来实现的,这样就可以省去
原函数 = 装饰器(原函数)
这一句代码,而直接调用原函数()这个函数就可以运行装饰器函数
fastAPI使用
第一个 FastAPI 应用 | 菜鸟教程 (runoob.com)
下载地址:tiangolo/fastapi:FastAPI 框架,高性能,简单易学,快速编码,准备生产 (github.com)
上传文件:
FastAPI(24)- 详解 File,上传文件 - 小菠萝测试笔记 - 博客园 (cnblogs.com)
实现上传一个文件之后读取文件名称、文件类型、文件大小
1.建立一个main.py文件
超全面整理fastAPI(从入门到运用),进来看十秒钟再走不迟-CSDN博客
里面的一些理解:
1.路径参数
就是比如访问127.0.0.1:8000/items/bsy的后面那段“/bsy"
也是代码里的:{item_id},可以随意变化
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
def read_item(item_id):
return {"item_id": item_id}
2.里面的2-2节中的async def
是用于定义异步函数的关键字
异步函数是 Python 异步编程的核心组成部分,它们允许代码在等待某些操作完成时(例如 I/O 操作)释放控制权,这样可以在单个线程内同时处理多个任务。
异步函数的定义如下:
async def function_name(parameters):
# 函数体
# 可以包含异步操作,如 await some_async_function()
以下是一些关于异步函数的要点:
-
异步函数返回一个协程(coroutine)对象:这意味着异步函数在调用时不会立即执行,而是返回一个可以等待的对象。
-
使用
await
关键字:在异步函数内部,可以使用await
关键字来调用其他异步函数或协程。await
会挂起当前协程的执行,直到被调用的协程完成。 -
事件循环:异步函数需要一个事件循环来运行。事件循环负责调度和执行协程,以及处理 I/O 事件。
-
不能在同步代码中直接调用异步函数:异步函数只能从另一个异步函数中使用
await
调用,或者通过事件循环的run_until_complete()
方法运行。
以下是一个简单的异步函数示例:
import asyncio
async def hello_world():
print("Hello")
await asyncio.sleep(1) # 模拟 I/O 操作
print("World")
# 在事件循环中运行异步函数
asyncio.run(hello_world())
在这个例子中,hello_world
是一个异步函数,它打印 “Hello”,然后等待 1 秒(模拟 I/O 操作),最后打印 “World”。使用asyncio.run()
函数来运行这个异步函数,它会创建一个新的事件循环,运行传入的协程,并在协程完成后关闭事件循环。
3.如果路径参数设置成整数:def read_item(item_id: int):
但是地址对应的不是整数的话会显示:
{
"detail": [
{
"type": "int_parsing",
"loc": [
"path",
"item_id"
],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "时代光华"
}
]
}
终端会显示:422 Unprocessable Entity
4.枚举类enum
from enum import Enum
from fastapi import FastAPI
class Hjx_Class_name(str, Enum):
Name = 'huangjunx'
Year = 18
Id = '20153201072'
student = True
app = FastAPI()
@app.get('/hjx/{hjx_man}')
def root(hjx_man: Hjx_Class_name):
return {'status': hjx_man}
例如你访问http://127.0.0.1:8001/hjx/20153201072,得到的会是:{“status”:“20153201072”}
例如你访问http://127.0.0.1:8001/hjx/True,得到的会是:{“status”:“True”}
这样我们就能做到给某个路径参数传递某几个固定的有效值了。
进一步,我们还可以在root函数里面调用这个类的类属性。通过Hjx_Class_name.Name进行调用。下面例子无论你使用哪个类属性的值访问,结果都是{"status":"huangjunx"}
4.2-4节中路径参数的值是路径类型变量的意思是
假设现在你有一个路径操作:/files/{file_path},但是你需要 file_path 本身包含一个 路径, 比如 home/johndoe/myfile.txt.
因此, 文件路径可能是: /files/home/johndoe/myfile.txt
在这种情况,我们使用Path转换器就可以进行转换了。使用以下方法声明值是路径的路径参数。
from fastapi import FastAPI
app = FastAPI()
@app.get("/files/{file_path:path}")
def read_user_me(file_path):
return {"file_path": file_path}
语句表示的意思是:参数的名字是 file_path
,:path
说明参数file_path
对应的类型是 path
类型
5.查询参数:
当你声明不属于路径参数的其他函数参数时,它们将自动解释为“Query”参数,也就是查询参数。
查询参数就是一系列在URL?
之后的key-value键值对,每对键值对用 &
分割开来
from fastapi import FastAPI
app = FastAPI()
@app.get("/files/")
def add(num1: int=2, num2 int=8):
return {"num1 + num2 = ": num1 + num2}#默认输出结果是10
当你使用浏览器访问http://127.0.0.1:8001/files/?num1=2&num2=3,你会得到:{"num1 + num2 = ":5}
声明可选的Query参数,只需要将他们的默认值设置为None
即可。
关于查询参数还需要注意以下几点:
- 如果设置的查询参数没有默认值不是None,那么这个查询参数就是必需查询参数,必须要传入,否则会报错
- 查询参数可以和路径参数结合使用
6.请求体和响应体
请求体是客户端发送到您的API的数据。 响应体是您的API发送给客户端的数据。
API几乎总是必须发送一个响应体,但是客户端并不需要一直发送请求体。
定义请求体,需要使用 Pydantic 模型。注意以下几点
- 不能通过GET请求发送请求体
- 发送请求体数据,必须使用以下几种方法之一:POST(最常见)、PUT、DELETE、PATCH
7.刚刚指明参数类为int等类型,还可以自己定义类:
from fastapi import FastAPI
from pydantic import BaseModel
#声明了一个JSON对象
class Item(BaseModel):
name: str
description: str = None
price: float
tax: float = None
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item):
return item
定义了一个Item类,和查询参数一样:数据类型的属性如果不是必须的话,可以拥有一个默认值或者是可选None。否则,该属性就是必须的。
所以访问的请求体就可以是:
{
"name": "Foo",
"description": "An optional description",
"price": 45.2,
"tax": 3.5
}
或者
{
"name": "Foo",
"price": 45.2
}
8.参数排序技巧:
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
*, item_id: int = Path(..., title="The ID of the item to get"), q: str ):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
此时输入的网址就可以是http://127.0.0.1:8000/items/434?q=9
网址就会输出
{
"item_id": 434,
"q": "9"
}
9.还可以给item_id做验证
改变这一行:
*, item_id: int = Path(..., title="The ID of the item to get")
*, item_id: int = Path(..., title="The ID of the item to get", ge=1)
意思是大于或者等于ge
,只能比较整数。例如大于等于1
item_id: int = Path(..., title="The ID of the item to get", gt=0, le=1000)
小于或者等于le
,只能比较整数。例如大于等于0小于等于1000
item_id: int = Path(..., title="The ID of the item to get", ge=0, le=1000)
大于gt
, 小于lt
,可以比较浮点数和整数
10.各种参数的总理解
这里混合使用了路径参数item_id,查询参数q,请求体参数item。第一个参数星号表示星号之后所有参数都应称为关键字参数(键-值对),即使它们没有默认值。这是一种有效的写法。
from fastapi import FastAPI, Path
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str = None
price: float
tax: float = None
@app.put("/items/{item_id}")
async def update_item(
*,
item_id: int = Path(..., title="The ID of the item to get", ge=0, le=1000),
q: str = None,
item: Item = None,
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
if item:
results.update({"item": item})
return results
11.使用Body方法定义单值的请求体参数
例如,扩展之前的模型,之前的模型还需要额外增加一个参数:除去item和user这两个字段之外,还需要importance在请求体中,如果你直接定义它,因为他是一个单值,FastAPI会默认将其定义为query参数。
但是你可以使用Body方法,让FastAPI将其视为请求体的key:
importance: int = Body(...)
注意一点:Schema
与 Query
,Path
和Body
的工作方式相同,具有相同的参数
schema是定义类属性里参数的范围,跟刚刚path函数语法一样不过需要导入
from pydantic import BaseModel, Schema
8-2节还可以给请求体展示示例,在声明函数输入的时候加上
example={ "name": "Foo", "description": "A very nice Item", "price": 35.4, "tax": 3.2, },
当我们访问/docs
,我们将看到如下:Example Value | Schema 数据示例,告诉用户,这个api想要什么样格式的请求体格式数据。
12.第九节的Body - Nested Models(嵌套模型)
其实就是类似于集合里面的元素也可以是集合
==我们可以将请求体的属性值类型设置为列表、元组,列表里面的元素可以是正常的数据类型,也可以是请求体模型。当然,请求体的属性值也可以设置为请求体模型。==这种现象我们称之为模型的嵌套。
from typing import List,Set
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
#嵌套的类:
class Image(BaseModel):
url: UrlStr
name: str
class Item(BaseModel):
name: str
description: str = None
price: float
tax: float = None
#将属性值的类型定义为列表
# tags: list = []
#进一步定义列表里面元素的类型为整数
# tags: List[int] = []
tags: Set[str] = [] # 表示属性值的类型为元组,元组中元素的类型为字符串
#表示类型是image
images: List[Image] = None
@app.put("/items/{item_id}")
async def update_item(*, item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
UrlStr
表示url格式类型的字符串。
例如"http://example.com/baz.jpg",“http://example.com/dave.jpg”
此时期望的请求体格式为:
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2,
"tags": [
"rock",
"metal",
"bar"
],
"images": [
{
"url": "http://example.com/baz.jpg",
"name": "The Foo live"
},
{
"url": "http://example.com/dave.jpg",
"name": "The Baz"
}
]
}
小结:
我们可以为请求体的属性值定义不同的类型,可以是列表,可以是元组,列表或者元组里面的元素可以是字符串或者是数值,还可以是请求体模型。这种结构给fastAPi传递参数带来很大的方便性。
13.Response Model(响应模型)
可以在任何路径操作中使用参数 response_model
声明用于响应的模型:
- @app.get():会向数据库发索取数据的请求,从而来获取信息,该请求就像数据库的select操作一样,只是用来查询一下数据,不会修改、增加数据,不会影响资源的内容,即该请求不会产生副作用。无论进行多少次操作,结果都是一样的。
- @app.post():POST请求同PUT请求类似,都是向服务器端发送数据的,但是该请求会改变数据的种类等资源,就像数据库的insert操作一样,会创建新的内容。几乎目前所有的提交操作都是用POST请求的。
- @app.put():PUT请求是向服务器端发送数据的,从而改变信息,该请求就像数据库的update操作一样,用来修改数据的内容,但是不会增加数据的种类等,也就是说无论进行多少次PUT操作,其结果并没有不同。
- @app.delete():用来删除某一个资源的,该请求就像数据库的delete操作
浅谈HTTP中Get、Post、Put与Delete的区别_联想如何跳过联网-CSDN博客
既然PUT和POST操作都是向服务器端发送数据的,那么两者有什么区别呢。。。POST主要作用在一个集合资源之上的(url),而PUT主要作用在一个具体资源之上的(url/xxx),通俗一下讲就是,如URL可以在客户端确定,那么可使用PUT,否则用POST。
综上所述,我们可理解为以下:
1、POST /url 创建
2、DELETE /url/xxx 删除
3、PUT /url/xxx 更新
4、GET /url/xxx 查看
在 FastAPI 中,app.get() 、app.post()等方法用于定义 HTTP 请求的路由。这些方法接受多个参数,用于指定路由的路径、请求处理函数、依赖项等。
以下是主要参数:
path(必填):用于指定路由的路径。这是一个字符串,表示 URL 路径,可以包含路径参数和查询参数。
response_model:用于指定响应模型。响应模型是响应数据的数据结构,通常使用 Pydantic 模型来定义。
summary:一个简短的字符串,用于描述路由的目的或功能。
description:用于提供更详细的路由说明。
tags:一个字符串列表,用于标记路由,以便在文档中分类和组织路由。
dependencies:一个列表,指定在路由处理函数中需要注入的依赖项。
response_description:用于指定响应的描述信息。
deprecated:一个布尔值,表示路由是否已被弃用。
status_code:用于指定响应的 HTTP 状态码。
response_model_include 和 response_model_exclude:用于指定在响应模型中包含或排除的字段。
response_model_by_alias:一个布尔值,表示是否使用 Pydantic 模型中的别名来序列化响应。
response_model_exclude_unset:一个布尔值,表示在响应中排除未设置的字段。
response_model_exclude_defaults:一个布尔值,表示在响应中排除具有默认值的字段。
response_model_exclude_none:一个布尔值,表示在响应中排除值为 None 的字段。
operation_id:用于指定操作的唯一标识符。
deprecated:一个布尔值,表示路由是否已被弃用。
callbacks:一个字典,用于指定回调函数。
这些参数可以根据需求来灵活配置。一般来说,path 参数是必需的,而其他参数则根据需要来选择性地使用。
原文链接:https://blog.csdn.net/footless_bird/article/details/134242229
最终操作:
(1)创建main.py文件:(这里有多个接口)
###############################参数排序技巧:此时输入的网址就可以是http://127.0.0.1:8000/items/434?q=9
from fastapi import FastAPI, Path
from pydantic import BaseModel
app = FastAPI()
@app.get("/items2/{item_id}")
async def read_items(
*, item_id: int = Path(..., title="The ID of the item to get"), q: str ):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
###############################使用请求体,关键是app.post,不过这个之后网页就输出:没有细节了,是post的原因
# from fastapi import FastAPI
# from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str = None
price: float
tax: float = None
# app = FastAPI()
@app.post("/items3/")
async def create_item(item: Item):
return item.name
###############################声明路径参数的类型为str
# from fastapi import FastAPI
# app = FastAPI()
@app.get("/items/{item_name}")
def read_item(item_name: str):
return {"item_id": item_name}
##############################声明路径参数的类型为int,并创建异步函数async def
# from fastapi import FastAPI
# app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id: int):
return {"item_id": item_id}
###############################声明路径参数
# from fastapi import FastAPI
# app = FastAPI()
@app.get("/items/{item_id}")
def read_item(item_id):
return {"item_id": item_id}
##############################了解FastAPI程序结构
# from fastapi import FastAPI
# app = FastAPI()
@app.get("/data/bsy/")
async def root():
return {"message": "Hello World"}
(2)使用终端开启uvicorn
服务
uvicorn main:app --reload
或者在main的代码后面加上
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="127.0.0.1", port=8000)
(3)运行成功会显示:
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [8438] using statreload
INFO: Started server process [8440]
INFO: Waiting for application startup.
INFO: Application startup complete.
(4)测试:
可以输入一些网址看看是不是对应输出:
127.0.0.1:8000/items/hkhje3342
{
"item_id": "hkhje3342"
}
127.0.0.1:8000/items2/32124?q=8
{
"item_id": 32124,
"q": "8"
}
实现能上传文件并保存到本地:
文件类型辨析
UploadFile
与 bytes
相比有更多优势:
- 使用
spooled
文件:- 存储在内存的文件超出最大上限时,FastAPI 会把文件存入磁盘;
- 这种方式更适于处理图像、视频、二进制文件等大型文件,好处是不会占用所有内存;
- 可获取上传文件的元数据;
- 自带 file-like
async
接口; - 暴露的 Python
SpooledTemporaryFile
对象,可直接传递给其他预期「file-like」对象的库。
UploadFile
的属性如下:
filename
: 上传文件名字符串(str
),例如,myimage.jpg
;content_type
: 内容类型(MIME 类型 / 媒体类型)字符串(str
),例如,image/jpeg
;file
:SpooledTemporaryFile
( file-like 对象)。 其实就是 Python文件,可直接传递给其他预期file-like
对象的函数或支持库。
UploadFile
支持以下 async
方法,(使用内部 SpooledTemporaryFile
)可调用相应的文件方法。
write(data)
: 把data
(str
或bytes
)写入文件;read(size)
: 按指定数量的字节或字符(size
(int
))读取文件内容;seek(offset)
: 移动至文件offset
(int
)字节处的位置;- 例如,
await myfile.seek(0)
移动到文件开头; - 执行
await myfile.read()
后,需再次读取已读取内容时,这种方法特别好用;
- 例如,
close()
: 关闭文件。
因为上述方法都是 async
方法,要搭配「await」使用。
例如,在 async
路径操作函数 内,要用以下方式读取文件内容:
contents = await myfile.read()
代码:
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile = File(...)):
# 保存文件到本地
with open("xx23.png", "wb") as f:
f.write(file)
contents = await file.read()
return {"filename": file.filename, "contents": contents}
# 路由操作函数上传一个文件并读取文件名称
@app.post("/files/")
async def create_file(file: bytes = File(...)):
k=file
# 保存文件到本地
with open("xx.png", "wb") as f:
f.write(file)
return {"16_num": k[0],
"file_size": len(file)}
#k.hex()是bytes类型k的16进制数,可以使用索引和切片来访问bytes对象,就像字符串和列表一样。
测试:在http://127.0.0.1:8000/docs网页找到接口
点击右下角,然后上传文件cope.png(随便一个)
运行,之后本地绝对路径下就有了:
利用FunASR和fastapi创建一个语音识别的接口
from fastapi import FastAPI, File, UploadFile
from fastapi.responses import HTMLResponse
from funasr import AutoModel
app = FastAPI()
@app.get("/", response_class=HTMLResponse)
async def server():
return """
<html>
<head>
<title>File Upload</title>
</head>
<body>
<form action="http://localhost:8000/upload" method="post" enctype="multipart/form-data">
<input type="file" name="file" />
<input type="submit" value="Upload" />
</form>
</body>
</html>
"""
# # <body>
# <h1>Fast api</h1>
# <button onclick="alert('您点击了按钮')">点击我</button>
# </body>
@app.post("/upload")
async def upload_file(file: UploadFile = File(...)):
file_location = f"uploaded_files/{file.filename}"
with open(file_location, "wb+") as file_object:
file_object.write(await file.read())
model = AutoModel(model="paraformer-zh", vad_model="fsmn-vad", punc_model="ct-punc")
# spk_model="cam++"
res = model.generate(input=file_location, batch_size_s=300, hotword='魔搭')
# print(res)
return {"filename": file.filename, "message": "File successfully uploaded","results":res}
然后登录http://localhost:8000/上传文件:来源:好心分手(30秒版)(翻自 卢巧音) -王小帅-mp3免费在线下载播放-歌曲宝-找歌就用歌曲宝-MP3音乐高品质在线免费下载 (gequbao.com)
等一段时间,输出结果:
{
"filename": "song.mp3",
"message": "File successfully uploaded",
"results": [
{
"key": "song",
"text": "回头吧,不要走,不要这样离开我太多,结果往事中一失折磨,沮丧,挥去那一把愈发热两发搭不要伤一转头,好身人重过过,全都要飞扬光空通奔都唱着,这首歌会让他们分手,你是否原谅我?",
"timestamp": [
[
790,
1030
],
[
1070,
1310
],等等。。
笑死,感觉识别得还不错,一般般。
原文:回头吧,不要走,不要这样离开我,恨太多,没结果,往事重提是折磨,下半生,陪着你,怀疑快乐也不多,没有心,别在拖,好心一早放开我,从头努力也坎坷,统统不要好过,为何唱着这首歌,为怨恨而分手,问你是否原谅我?
识别结果:回头吧,不要走,不要这样离开我太多,结果往事中一失折磨,沮丧,挥去那一把愈发热两发搭不要伤一转头,好身人重过过,全都要飞扬光空通奔都唱着,这首歌会让他们分手,你是否原谅我?
正确字符数:大约 70-80(这需要手动或程序来确定,由于文本差异较大,这里只给出一个估计)
现在,我们可以计算正确率:
正确率 = (正确字符数 / 原始文本的字符总数) * 100%
正确率 = (75 / 135) * 100% ≈ 55.56%
估计是因为粤语,我听得也是识别出来的那种音哈哈。
但是不能识别.m4a文件会报错:Internal Server Error
中文识别正确率大概98%左右,主要是”的“和”地“分辨不出来,就把”和“识别成”个“,绕口令也能识别出来,正确率99%,英文识别率75%左右,有些音相同的容易识别错。
千问1.5代码生成微调
参考这一篇,在linux上运行
# Experimental Environment: A100
# GPU Memory Requirement: 20GB
# Runtime: 3.1 hours
CUDA_VISIBLE_DEVICES=0 \
swift sft \
--model_type qwen1half-7b-chat \
--dataset blossom-math-zh \
--num_train_epochs 5 \
--sft_type lora \
--output_dir output \
--eval_steps 200 \
感觉数据集是魔搭平台的blossom-math-v2,但是跟上述的名称不太一样,难道是只是里面的中文包?,那个数据集有10004条,主要是一些数学问题
要跑好久,目前进度:
大模型微调包含以下四个核心步骤:
数据准备:
选择与任务相关的数据集。
对数据进行预处理,包括清洗、分词、编码等。
选择基础模型:
选择一个预训练好的大语言模型,如BERT、GPT-3等。
设置微调参数:
设定学习率、训练轮次(epochs)、批处理大小(batch size)等超参数。
根据需要设定其他超参数,如权重衰减、梯度剪切等。
微调流程:
加载预训练的模型和权重。
根据任务需求对模型进行必要的修改,如更改输出层。
选择合适的损失函数和优化器。
使用选定的数据集进行微调训练,包括前向传播、损失计算、反向传播和权重更新。
原文链接:https://blog.csdn.net/qq_39172059/article/details/136693607
每天一些linux操作小知识
查看文件安装路径:
由于软件安装的地方不止一个地方,所有先说查看文件安装的所有路径(地址)。
这里以hbase为例。比如说我安装了Oracle,但是不知道文件都安装在哪些地方、放在哪些文件夹里,可 以用下面的命令查看所有的文件路径
在终端输入:
whereis hbase
回车,如果你安装好了hbase,就会显示文件安装的地址,例如我的显示(安装地址可能会不同)
hbase: /usr/bin/hbase /etc/hbase
可以看出来,hbase安装在是个目录里。
如果你没有安装hbase或者hbase安装没成功,则不会显示文件路径出来。只提示:
hbase:
二、查询运行文件所在路径:
如果你只要查询文件的运行文件所在地址,直接用下面的命令就可以了(还是以Oracle为例):
which oracle
结果会显示:
/usr/bin/oracle
来源:Linux新手入门如何知道程序安装在了哪里_linux查看安装程序根-CSDN博客
零散记录:
mobaxterm内拖动文件夹就可以实现windows本地上传文件到linux
huggingface网页可以搜到很多数据集是NLP学习者的一个必备平台
豆包(抖音登录就行)大模型很好用,就是图片生成可能复杂一点就不是我们想要的
Snipaste很好用还免费,可以按F1截图,然后F3提取出来,放在界面之首,方便查看