基于零一万物大模型的图像内容搜索

实现步骤如下:

  1. 通过大模型生成图片描述,然后写入数据库;
  2. 将图片描述+文本OCR+图片的标签合并成一句话,然后再通过m3e-base模型将文本变成向量化,写入milvus数据库;
  3. 再通过fastapi读取milvus实现api服务,返回文本描述最相似的图片。

第一步,下面是通过零一万物的api接口和CogAgent-Chat两种方式生成图片描述,CogAgent-Chat需要GPU(13G以上)

import torch
#from PIL import Image
#from modelscope import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
#import argparse

import os
import requests
import time

#from paddleocr import PaddleOCR

import utils
#import baidu_trans as baiduTrans

import openai
from openai import OpenAI

API_BASE = "https://api.lingyiwanwu.com/v1"
API_KEY = "token"

client = OpenAI(
    # defaults to os.environ.get("OPENAI_API_KEY")
    api_key=API_KEY,
    base_url=API_BASE
)

#parser = argparse.ArgumentParser()
#parser.add_argument("--quant", choices=[4, 8], type=int, default=None, help='quantization bits')
#parser.add_argument("--from_pretrained", type=str, default="/root/autodl-fs/ZhipuAI/cogagent-chat",
#                    help='pretrained ckpt')
#parser.add_argument("--local_tokenizer", type=str, default="/root/autodl-fs/AI-ModelScope/vicuna-7b-v1___5",
#                    help='tokenizer path')
## parser.add_argument("--local_tokenizer", type=str, default="/root/autodl-fs/ZhipuAI/chatglm2-6b", help='tokenizer path')
#parser.add_argument("--fp16", action="store_true")
#parser.add_argument("--bf16", action="store_true")
#
#args, unknown = parser.parse_known_args()
#
#MODEL_PATH = args.from_pretrained
#TOKENIZER_PATH = args.local_tokenizer
#DEVICE = 'cuda' if torch.cuda.is_available() else 'cpu'
#
#tokenizer = AutoTokenizer.from_pretrained(TOKENIZER_PATH)
#if args.bf16:
#    torch_type = torch.bfloat16
#else:
#    torch_type = torch.float16
#
#print("========Use torch type as:{} with device:{}========\n\n".format(torch_type, DEVICE))
#
## quantization configuration for NF4 (4 bits)
#bnb_config = BitsAndBytesConfig(
#    load_in_4bit=True,
#    bnb_4bit_use_double_quant=True,
#    bnb_4bit_quant_type="nf4",
#    bnb_4bit_compute_dtype=torch.bfloat16
#)
#
#if args.quant:
#    model = AutoModelForCausalLM.from_pretrained(
#        MODEL_PATH,
#        torch_dtype=torch_type,
#        low_cpu_mem_usage=True,
#        quantization_config=bnb_config,
#        trust_remote_code=True
#    ).eval()
#else:
#    model = AutoModelForCausalLM.from_pretrained(
#        MODEL_PATH,
#        torch_dtype=torch_type,
#        low_cpu_mem_usage=True,
#        load_in_4bit=args.quant is not None,
#        trust_remote_code=True
#    ).to(DEVICE).eval()
#
#ocr = PaddleOCR(use_angle_cls=True, lang="ch")
    
conn = utils.db_connect("online")
sql = "select * from img_source where status=1"
res = conn.select(sql)

for v in res:
    image_id = v['id']
    print(image_id)
    if v['path'] is None:
        continue
    splitArr = v['path'].split(".")
    cdnUrl = "https://cdn.vcbeat.top/" + v['path']
    # print(cdnUrl)
#    file = "image/" + str(image_id) + "." + splitArr[1]
#    print(file)
#    file_input = requests.get(cdnUrl)
#    os.makedirs('image', exist_ok=True)
#    with open(file, 'wb') as f:
#        f.write(file_input.content)
#        f.close()
#
#    image = Image.open(file).convert('RGB')
#    history = []
#    query = "Provide a detailed description of what is depicted in this picture"
#    input_by_model = model.build_conversation_input_ids(tokenizer, query=query, history=history, images=[image])
#    inputs = {
#        'input_ids': input_by_model['input_ids'].unsqueeze(0).to(DEVICE),
#        'token_type_ids': input_by_model['token_type_ids'].unsqueeze(0).to(DEVICE),
#        'attention_mask': input_by_model['attention_mask'].unsqueeze(0).to(DEVICE),
#        'images': [[input_by_model['images'][0].to(DEVICE).to(torch_type)]],
#    }
#    if 'cross_images' in input_by_model and input_by_model['cross_images']:
#        inputs['cross_images'] = [[input_by_model['cross_images'][0].to(DEVICE).to(torch_type)]]
#
#    # add any transformers params here.
#    gen_kwargs = {"max_length": 2048,
#                  "temperature": 0.9,
#                  "do_sample": False}
#    with torch.no_grad():
     #outputs = model.generate(**inputs, **gen_kwargs)
     #outputs = outputs[:, inputs['input_ids'].shape[1]:]
     #response = tokenizer.decode(outputs[0])
     #response = response.split("</s>")[0]
     #print("\nCog:", response)

     #content = baiduTrans.common_translate(response)
     #
     #result = ocr.ocr(file, cls=True)
     #texts = []
     #for idx in range(len(result)):
     #    res = result[idx]
     #    if res is not None:
     #       for line in res:
     #           texts.append(line[1][0])
     #ocr_desc = ",".join(texts)
    content = [
    {"type":"image_url","image_url":{"url":cdnUrl}},
    {"type":"text","text":"这张图片描述了什么内容,如果图片上面有文字,请同时回复文字的内容"}
    ]
    completion = client.chat.completions.create(
        model="yi-vl-plus",
        messages=[{"role": "user", "content": content}]
    )
    content = completion.choices[0].message.content
    print(content)
    fields = {
        "ai_desc": content,
    #    "ocr_desc":ocr_desc
    }
    utils.update_data(conn,"img_source", image_id, fields)
    time.sleep(30)

总结:注释部分是通过CogAgent-Chat大模型生成的图片描述;对比发现零一万物的图片描述更加丰富(但是也有幻觉的数据);

第二步,写入mivus数据库脚本

import time
import os
import sys

sys.path.append(os.path.abspath(os.path.join(__file__, "..", "..")))

from sentence_transformers import SentenceTransformer

import utils
from milvus.milvus_ann_search import search, insert, query

def run():
    conn_online = utils.db_connect("online")
    tokenizer = SentenceTransformer('./m3e-base')
    utils.milvus_connect("milvus")

    sql = "select * from img_source where status=1 and path IS NOT NULL"
    res = conn_online.select(sql)
    for v in res:
        print(v['id'])
        content = []
        if v['title'] is not None:
            content.append(v['title'])
        if v['ocr_desc'] != "":
            content.append(v['ocr_desc'])
        if v['ai_desc'] != "":
            content.append(v['ai_desc'])
        desc = ",".join(content)

        all_embeddings = tokenizer.encode([desc])
        collection_name = "img_source_new"
        # app_mi_id = 1
        now = time.time()
        create_time = int(now)
        data = [
            [all_embeddings[0].tolist()],
            [v['id']],
            [create_time]
        ]
        mr = insert(collection_name, data)
        print(mr)
run()

第三步,提供API服务,返回最相似的20张图片

import uvicorn
from fastapi import FastAPI
from pydantic import BaseModel

from sentence_transformers import SentenceTransformer
import utils
from milvus.milvus_ann_search import search, insert, query

conn_online = utils.db_connect("online")
tokenizer = SentenceTransformer('./m3e-base')
utils.milvus_connect("milvus")

class SearchData(BaseModel):
    search: str = ""

app = FastAPI()

# 图片搜索
@app.post("/get_images/")
async def get_images(topic: SearchData):
    search_text = topic.search
    print("search=" + search_text)
    collection_name = "img_source_new"
    all_embeddings = tokenizer.encode([search_text])
    anns_field = "desc_vec"
    results = search(collection_name, anns_field, all_embeddings, 20, None, ['id', 'img_id'])
    hit_ids = []
    hit_dict = {}
    arr_id = []
    for hits in results:
        for hit in hits:
            hit_ids.append(hit.id)
            distance = hit.distance
            hit_dict[hit.id] = distance
            img_id = hit.entity.get('img_id')
            # print(img_id)
            arr_id.append((img_id, distance))
            print("img_id={}\tdistance={}".format(img_id, distance))
    return {"msg": "success", "code": 200, "response": arr_id}

if __name__ == "__main__":
    uvicorn.run(app, host='0.0.0.0', port=8100)

测试结果如下,也可以通过distance来控制返回图片的相似度,距离太远的可以不返回。


image.png
image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 198,082评论 5 464
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,231评论 2 375
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 145,047评论 0 327
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,977评论 1 268
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,893评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,014评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,976评论 3 388
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,605评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,888评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,906评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,732评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,513评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,980评论 3 301
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,132评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,447评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,027评论 2 343
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,232评论 2 339

推荐阅读更多精彩内容