通过阿里云Milvus与通义千问VL大模型,快速实现多模态搜索

news2025/4/25 15:14:28

本文主要演示了如何使用阿里云向量检索服务Milvus版与通义千问VL大模型,提取图片特征,并使用多模态Embedding模型,快速实现多模态搜索。

基于灵积(Dashscope)模型服务上的通义千问 API以及Embedding API来接入图片、文本等非结构化数据Embedding为向量的能力。

通义千问VL大模型介绍《通义千问VL API详情》

通义多模态向量模型介绍《Multimodal-Embedding API详情》

前提条件

  • 已创建阿里云Milvus实例。具体操作,请参见快速创建Milvus实例。

  • 已开通服务并获得API-KEY。具体操作,请参见开通DashScope并创建API-KEY。

该示例的运行环境为python3.9

python3 -m pip install dashscope pymilvus==2.5.0

wget https://github.com/milvus-io/pymilvus-assets/releases/download/imagedata/reverse_image_search.zip
unzip -q -o reverse_image_search.zip

示例代码

在本文示例中,我们先将示例图片通过通义千问VL提取图片描述,存储在image_description中,然后将图片描述和图片通过多模态Embedding模型分别转换为向量存储在image_embedding和text_embedding中。

备注:在该示例中,仅以数据集前200张图片作为演示

import base64
import csv
import dashscope
import os
import pandas as pd
import sys
import time
from tqdm import tqdm
from pymilvus import (
    connections,
    FieldSchema,
    CollectionSchema,
    DataType,
    Collection,
    MilvusException,
    utility,
)

from http import HTTPStatus
import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


class FeatureExtractor:
    def __init__(self, DASHSCOPE_API_KEY):
        self._api_key = DASHSCOPE_API_KEY  # 使用环境变量存储API密钥

    def __call__(self, input_data, input_type):
        if input_type not in ("image", "text"):
            raise ValueError("Invalid input type. Must be 'image' or 'text'.")

        try:
            if input_type == "image":
                _, ext = os.path.splitext(input_data)
                image_format = ext.lstrip(".").lower()
                with open(input_data, "rb") as image_file:
                    base64_image = base64.b64encode(image_file.read()).decode("utf-8")
                input_data = f"data:image/{image_format};base64,{base64_image}"
                payload = [{"image": input_data}]
            else:
                payload = [{"text": input_data}]

            resp = dashscope.MultiModalEmbedding.call(
                model="multimodal-embedding-v1",
                input=payload,
                api_key=self._api_key,
            )

            if resp.status_code == HTTPStatus.OK:
                return resp.output["embeddings"][0]["embedding"]
            else:
                raise RuntimeError(
                    f"API调用失败,状态码: {resp.status_code}, 错误信息: {resp.message}"
                )
        except Exception as e:
            logger.error(f"处理失败: {str(e)}")
            raise


class FeatureExtractorVL:
    def __init__(self, DASHSCOPE_API_KEY):
        self._api_key = DASHSCOPE_API_KEY  # 使用环境变量存储API密钥

    def __call__(self, input_data, input_type):
        if input_type not in ("image"):
            raise ValueError("Invalid input type. Must be 'image'.")

        try:
            if input_type == "image":
                payload=[
                            {
                                "role": "system",
                                "content": [{"type":"text","text": "You are a helpful assistant."}]
                            },
                            {
                                "role": "user",
                                "content": [
                                            # {"image": "https://dashscope.oss-cn-beijing.aliyuncs.com/images/dog_and_girl.jpeg"},
                                            {"image": input_data},
                                            {"text": "先用50字内的文字描述这张图片,然后再给出5个关键词"}
                                            ],
                            }
                        ]

            resp = dashscope.MultiModalConversation.call(
                model="qwen-vl-plus",
                messages=payload,
                api_key=self._api_key,
            )

            if resp.status_code == HTTPStatus.OK:
                return resp.output["choices"][0]["message"].content[0]["text"]
            else:
                raise RuntimeError(
                    f"API调用失败,状态码: {resp.status_code}, 错误信息: {resp.message}"
                )
        except Exception as e:
            logger.error(f"处理失败: {str(e)}")
            raise


class MilvusClient:
    def __init__(self, MILVUS_TOKEN, MILVUS_HOST, MILVUS_PORT, INDEX, COLLECTION_NAME):
        self._token = MILVUS_TOKEN
        self._host = MILVUS_HOST
        self._port = MILVUS_PORT
        self._index = INDEX
        self._collection_name = COLLECTION_NAME

        self._connect()
        self._create_collection_if_not_exists()

    def _connect(self):
        try:
            connections.connect(alias="default", host=self._host, port=self._port, token=self._token)
            logger.info("Connected to Milvus successfully.")
        except Exception as e:
            logger.error(f"连接Milvus失败: {str(e)}")
            sys.exit(1)

    def _collection_exists(self):
        return self._collection_name in utility.list_collections()
    
    def _create_collection_if_not_exists(self):
        try:
            if not self._collection_exists():
                fields = [
                    FieldSchema(name="id", dtype=DataType.INT64, is_primary=True, auto_id=True),
                    FieldSchema(name="origin", dtype=DataType.VARCHAR, max_length=512),
                    FieldSchema(name="image_description", dtype=DataType.VARCHAR, max_length=1024),
                    FieldSchema(name="image_embedding", dtype=DataType.FLOAT_VECTOR, dim=1024),
                    FieldSchema(name="text_embedding", dtype=DataType.FLOAT_VECTOR, dim=1024)
                ]

                schema = CollectionSchema(fields)

                self._collection = Collection(self._collection_name, schema)

                if self._index == 'IVF_FLAT':
                    self._create_ivf_index()
                else:
                    self._create_hnsw_index()   
                logger.info("Collection created successfully.")
            else:
                self._collection = Collection(self._collection_name)
                logger.info("Collection already exists.")
        except Exception as e:
            logger.error(f"创建或加载集合失败: {str(e)}")
            sys.exit(1)


    def _create_ivf_index(self):
        index_params = {
            "index_type": "IVF_FLAT",
            "params": {
                        "nlist": 1024, # Number of clusters for the index
                    },
            "metric_type": "L2",
        }
        self._collection.create_index("image_embedding", index_params)
        self._collection.create_index("text_embedding", index_params)
        logger.info("Index created successfully.")

    def _create_hnsw_index(self):
        index_params = {
            "index_type": "HNSW",
            "params": {
                        "M": 64, # Maximum number of neighbors each node can connect to in the graph
                        "efConstruction": 100, # Number of candidate neighbors considered for connection during index construction
                    },
            "metric_type": "L2",
        }
        self._collection.create_index("image_embedding", index_params)
        self._collection.create_index("text_embedding", index_params)
        logger.info("Index created successfully.")
    
    def insert(self, data):
        try:
            self._collection.insert(data)
            self._collection.load()
            logger.info("数据插入并加载成功.")
        except MilvusException as e:
            logger.error(f"插入数据失败: {str(e)}")
            raise

    def search(self, query_embedding, feild, limit=3):
        try:
            if self._index == 'IVF_FLAT':
                param={"metric_type": "L2", "params": {"nprobe": 10}}
            else:
                param={"metric_type": "L2", "params": {"ef": 10}}

            result = self._collection.search(
                data=[query_embedding],
                anns_field=feild,
                param=param,
                limit=limit,
                output_fields=["origin", "image_description"],
            )
            return [{"id": hit.id, "distance": hit.distance, "origin": hit.origin, "image_description": hit.image_description} for hit in result[0]]
        except Exception as e:
            logger.error(f"搜索失败: {str(e)}")
            return None


def load_image_embeddings(extractor, extractorVL, csv_path):
    df = pd.read_csv(csv_path)
    image_embeddings = {}

    for image_path in tqdm(df["path"].tolist()[:200], desc="生成图像embedding"): # 仅用前100张图进行演示
        try:
            desc = extractorVL(image_path, "image")
            image_embeddings[image_path] = [desc, extractor(image_path, "image"), extractor(desc, "text")]
            time.sleep(1)  # 控制API调用频率
        except Exception as e:
            logger.warning(f"处理{image_path}失败,已跳过: {str(e)}")

    return [{"origin": k, 'image_description':v[0], "image_embedding": v[1], 'text_embedding': v[2]} for k, v in image_embeddings.items()]

数据准备

if __name__ == "__main__":
    MILVUS_HOST = "c-xxxxxxxxxxxx.milvus.aliyuncs.com"
    MILVUS_PORT = "19530"
    MILVUS_TOKEN = "root:password"

    COLLECTION_NAME = "multimodal_search"
    INDEX = "IVF_FLAT" # IVF_FLAT OR HNSW

    # Step1:初始化Milvus客户端
    milvus_client = MilvusClient(MILVUS_TOKEN, MILVUS_HOST, MILVUS_PORT, INDEX, COLLECTION_NAME)
  
    DASHSCOPE_API_KEY = ""
    # Step2:初始化千问VL大模型与多模态Embedding模型
    extractor = FeatureExtractor(DASHSCOPE_API_KEY)
    extractorVL = FeatureExtractorVL(DASHSCOPE_API_KEY)

    # Step3:将图片数据集Embedding后插入到Milvus
    embeddings = load_image_embeddings(extractor, extractorVL, "reverse_image_search.csv")
    milvus_client.insert(embeddings)

在Step1中将Collection创建完成后,可以在控制台登录Attu,查看Collection Schema信息,如下图所示。

在完成Step3后,可以在Attu中查看插入数据,此时图片数据已经过通义千问VL大模型提取描述特征,并Embedding为向量。

可以看出下图经过通义千问VL大模型提取后,文本总结为“站在海滩上的人穿着牛仔裤和绿色靴子。沙滩上有水迹覆盖。关键词:海滩、脚印、沙地、鞋子、裤子”,用文字非常形象的描述了这张图片的特征。

图:jean/n03594734_9714.JPEG

多模态量检索:以文搜图

在下面这个这个示例中,查询文本query为"棕色的狗",将query通过多模态向量模型Embedding以后,分别在image_embedding和text_embedding上进行以文搜图和以文搜文,可以得到不同的检索结果。

注意:由于大模型产出结果存在一定的随机性,本示例结果可能无法完全一致的复现。

if __name__ == "__main__":
    MILVUS_HOST = "c-xxxxxxxxxxxx.milvus.aliyuncs.com"
    MILVUS_PORT = "19530"
    MILVUS_TOKEN = "root:password"

    COLLECTION_NAME = "multimodal_search"
    INDEX = "IVF_FLAT" # IVF_FLAT OR HNSW

    # Step1:初始化Milvus客户端
    milvus_client = MilvusClient(MILVUS_TOKEN, MILVUS_HOST, MILVUS_PORT, INDEX, COLLECTION_NAME)
  
    DASHSCOPE_API_KEY = ""
    # Step2:初始化多模态Embedding模型
    extractor = FeatureExtractor(DASHSCOPE_API_KEY)

    # Step4:多模态搜索示例,以文搜图和以文搜文
    text_query = "棕色的狗"
    text_embedding = extractor(text_query, "text")
    text_results_1 = milvus_client.search(text_embedding, feild = 'image_embedding')
    logger.info(f"以文搜图查询结果: {text_results_1}")
    text_results_2 = milvus_client.search(text_embedding, feild = 'text_embedding')
    logger.info(f"以文搜文查询结果: {text_results_2}")

"""
以文搜图查询结果
{'id': 457336885198973657, 'distance': 1.338853359222412, 'origin': './train/Rhodesian_ridgeback/n02087394_9675.JPEG', 'image_description': '一张小狗站在地毯上的照片。它有着棕色的毛发和蓝色的眼睛。\n关键词:小狗、地毯、眼睛、毛色、站立'}, 
{'id': 457336885198973648, 'distance': 1.3568601608276367, 'origin': './train/Rhodesian_ridgeback/n02087394_6382.JPEG', 'image_description': '这是一只棕色的猎犬,耳朵垂下,脖子上戴着项圈。它正直视前方。\n\n关键词:狗、棕色、猎犬、耳朵、项链'},
{'id': 457336885198973655, 'distance': 1.3838427066802979, 'origin': './train/Rhodesian_ridgeback/n02087394_5846.JPEG', 'image_description': '两只小狗在毛毯上玩耍。一只狗躺在另一只上面,背景中有一个玩具熊。\n\n关键词:小狗、玩闹、毛毯、玩具熊、互动'}
"""

"""
以文搜文查询结果
[{'id': 457336885198973739, 'distance': 0.6969608068466187, 'origin': './train/mongoose/n02137549_7552.JPEG', 'image_description': '这是一张棕色的小动物的特写照片。它有着圆润的脸庞和大大的眼睛。\n\n关键词:小动物、棕毛、圆形脸、大眼、自然背景'}, 
{'id': 457336885198973648, 'distance': 0.7110348343849182, 'origin': './train/Rhodesian_ridgeback/n02087394_6382.JPEG', 'image_description': '这是一只棕色的猎犬,耳朵垂下,脖子上戴着项圈。它正直视前方。\n\n关键词:狗、棕色、猎犬、耳朵、项链'}, 
{'id': 457336885198973707, 'distance': 0.7725887298583984, 'origin': './train/lion/n02129165_19310.JPEG', 'image_description': '这是一张狮子的特写照片。它有着浓密的鬃毛和锐利的眼神。\n\n关键词:狮子、眼神、鬃毛、自然环境、野生动物'}
"""

多模态向量检索:以图搜文

在下面这个这个示例中,我们使用test中的狮子图片进行相似性检索,分别进行以图搜图和以图搜文。

图:lion/n02129165_13728.JPEG

注意:由于大模型产出结果存在一定的随机性,本示例结果可能无法完全一致的复现。

if __name__ == "__main__":
    MILVUS_HOST = "c-xxxxxxxxxxxx.milvus.aliyuncs.com"
    MILVUS_PORT = "19530"
    MILVUS_TOKEN = "root:password"

    COLLECTION_NAME = "multimodal_search"
    INDEX = "IVF_FLAT" # IVF_FLAT OR HNSW

    # Step1:初始化Milvus客户端
    milvus_client = MilvusClient(MILVUS_TOKEN, MILVUS_HOST, MILVUS_PORT, INDEX, COLLECTION_NAME)
  
    DASHSCOPE_API_KEY = ""
    # Step2:初始化多模态Embedding模型
    extractor = FeatureExtractor(DASHSCOPE_API_KEY)

    # Step5:多模态搜索示例,以图搜图和以图搜文
    image_query_path = "./test/lion/n02129165_13728.JPEG"
    image_embedding = extractor(image_query_path, "image")
    image_results_1 = milvus_client.search(image_embedding, feild = 'image_embedding')
    logger.info(f"以图搜图查询结果: {image_results_1}")
    image_results_2 = milvus_client.search(image_embedding, feild = 'text_embedding')
    logger.info(f"以图搜文查询结果: {image_results_2}")

"""
以图搜图查询结果
{'id': 457336885198973702, 'distance': 0.23892249166965485, 'origin': './train/lion/n02129165_19953.JPEG', 'image_description': '这是一只雄壮的狮子站在岩石旁,背景是树木和灌木丛。阳光洒在它的身上。\n\n关键词:狮子、岩石、森林、阳光、野性'},
{'id': 457336885198973704, 'distance': 0.4113130569458008, 'origin': './train/lion/n02129165_1142.JPEG', 'image_description': '一只狮子在茂密的绿色植物中休息。背景是竹子和树木。\n\n关键词:狮子、草地、绿植、树干、自然环境'}, 
{'id': 457336885198973699, 'distance': 0.5206397175788879, 'origin': './train/lion/n02129165_16.JPEG', 'image_description': '图中是一对狮子在草地上站立。雄狮鬃毛浓密,雌狮则显得更为瘦弱。\n\n关键词:狮子、草地、雄性、雌性、自然环境'}
"""

"""
以图搜文查询结果
{'id': 457336885198973704, 'distance': 1.0935896635055542, 'origin': './train/lion/n02129165_1142.JPEG', 'image_description': '一只狮子在茂密的绿色植物中休息。背景是竹子和树木。\n\n关键词:狮子、草地、绿植、树干、自然环境'}, 
{'id': 457336885198973702, 'distance': 1.2102885246276855, 'origin': './train/lion/n02129165_19953.JPEG', 'image_description': '这是一只雄壮的狮子站在岩石旁,背景是树木和灌木丛。阳光洒在它的身上。\n\n关键词:狮子、岩石、森林、阳光、野性'},
{'id': 457336885198973707, 'distance': 1.2725986242294312, 'origin': './train/lion/n02129165_19310.JPEG', 'image_description': '这是一张狮子的特写照片。它有着浓密的鬃毛和锐利的眼神。\n\n关键词:狮子、眼神、鬃毛、自然环境、野生动物'}
"""

import base64
import csv
import dashscope
import os
import pandas as pd
import sys
import time
from tqdm import tqdm
from pymilvus import (
    connections,
    FieldSchema,
    CollectionSchema,
    DataType,
    Collection,
    MilvusException,
    utility
)
from http import HTTPStatus
import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


class FeatureExtractor:
    def __init__(self):
        self._api_key = os.getenv("DASHSCOPE_API_KEY")  # 使用环境变量存储API密钥

    def __call__(self, input_data, input_type):
        if input_type not in ("image", "text"):
            raise ValueError("Invalid input type. Must be 'image' or 'text'.")

        try:
            if input_type == "image":
                _, ext = os.path.splitext(input_data)
                image_format = ext.lstrip(".").lower()
                with open(input_data, "rb") as image_file:
                    base64_image = base64.b64encode(image_file.read()).decode("utf-8")
                input_data = f"data:image/{image_format};base64,{base64_image}"
                payload = [{"image": input_data}]
            else:
                payload = [{"text": input_data}]

            resp = dashscope.MultiModalEmbedding.call(
                model="multimodal-embedding-v1",
                input=payload,
                api_key=self._api_key,
            )

            if resp.status_code == HTTPStatus.OK:
                return resp.output["embeddings"][0]["embedding"]
            else:
                raise RuntimeError(
                    f"API调用失败,状态码: {resp.status_code}, 错误信息: {resp.message}"
                )
        except Exception as e:
            logger.error(f"处理失败: {str(e)}")
            raise


class MilvusClient:
    def __init__(self):
        self._token = os.getenv("MILVUS_TOKEN")
        self._host = os.getenv("MILVUS_HOST")
        self._port = os.getenv("MILVUS_PORT", "19530")
        self._collection_name = "multimodal_search"

        self._connect()
        self._create_collection_if_not_exists()

    def _connect(self):
        try:
            connections.connect(alias="default", host=self._host, port=self._port, token=self._token)
            logger.info("Connected to Milvus successfully.")
        except Exception as e:
            logger.error(f"连接Milvus失败: {str(e)}")
            sys.exit(1)

    def _collection_exists(self):
        return self._collection_name in utility.list_collections()

    def _create_index(self):
        index_params = {
            "index_type": "IVF_FLAT",
            "params": {"nlist": 1024},
            "metric_type": "L2",
        }
        self._collection.create_index("embedding", index_params)
        logger.info("Index created successfully.")

    def _create_collection_if_not_exists(self):
        try:
            if not self._collection_exists():
                fields = [
                    FieldSchema(name="id", dtype=DataType.INT64, is_primary=True, auto_id=True),
                    FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=1024),
                    FieldSchema(name="origin", dtype=DataType.VARCHAR, max_length=512),
                ]
                schema = CollectionSchema(fields)
                self._collection = Collection(self._collection_name, schema)
                self._create_index()
                logger.info("Collection created successfully.")
            else:
                self._collection = Collection(self._collection_name)
                logger.info("Collection already exists.")
        except Exception as e:
            logger.error(f"创建或加载集合失败: {str(e)}")
            sys.exit(1)

    def insert(self, data):
        try:
            self._collection.insert(data)
            self._collection.load()
            logger.info("数据插入并加载成功.")
        except MilvusException as e:
            logger.error(f"插入数据失败: {str(e)}")
            raise

    def search(self, query_embedding, limit=10):
        try:
            result = self._collection.search(
                data=[query_embedding],
                anns_field="embedding",
                param={"metric_type": "L2", "params": {"nprobe": 10}},
                limit=limit,
                output_fields=["origin"],
            )
            return [{"id": hit.id, "distance": hit.distance, "origin": hit.origin} for hit in result[0]]
        except Exception as e:
            logger.error(f"搜索失败: {str(e)}")
            return None


def load_image_embeddings(extractor, csv_path):
    df = pd.read_csv(csv_path)
    image_embeddings = {}

    for image_path in tqdm(df["path"].tolist(), desc="生成图像嵌入"):
        try:
            image_embeddings[image_path] = extractor(image_path, "image")
            time.sleep(0.6)  # 控制API调用频率
        except Exception as e:
            logger.warning(f"处理{image_path}失败,已跳过: {str(e)}")

    return [{"origin": k, "embedding": v} for k, v in image_embeddings.items()]


def main():
    # 初始化Milvus客户端
    milvus_client = MilvusClient()

    # 初始化特征提取器
    extractor = FeatureExtractor()

    ## 1. 将图片数据集Embedding后插入到Milvus
    embeddings = load_image_embeddings(extractor, "reverse_image_search.csv")
    milvus_client.insert(embeddings)

    # 2. 执行搜索测试
    # 示例:以文搜图
    text_query = "木质折叠椅"
    text_embedding = extractor(text_query, "text")
    text_results = milvus_client.search(text_embedding)
    logger.info(f"以文搜图查询结果: {text_results}")

    # 示例:以图搜图
    image_query_path = "./test/Airedale/n02096051_4092.JPEG"
    image_embedding = extractor(image_query_path, "image")
    image_results = milvus_client.search(image_embedding)
    logger.info(f"以图搜图查询结果: {image_results}")


if __name__ == "__main__":
    main()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2342512.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用 Spring Boot Admin 通过图形界面查看应用配置信息的完整配置详解,包含代码示例和注释,最后以表格总结关键配置

以下是使用 Spring Boot Admin 通过图形界面查看应用配置信息的完整配置详解,包含代码示例和注释,最后以表格总结关键配置: 1. 环境准备 Spring Boot 版本:2.7.x(兼容 Spring Boot Admin 2.x)Spring Boot…

【计算机视觉】CV实战项目 - 基于YOLOv5与DeepSORT的智能交通监控系统:原理、实战与优化

基于YOLOv5与DeepSORT的智能交通监控系统:原理、实战与优化 一、项目架构与技术解析1.1 核心算法架构1.2 学术基础 二、实战环境配置2.1 硬件要求与系统配置2.2 分步安装指南 三、核心功能实战3.1 基础车辆计数3.2 自定义检测类别3.3 多区域计数配置 四、性能优化技…

17.磁珠在EMC设计中的运用

磁珠在EMC设计中的运用 1. 磁珠的高频等效特性2. 磁珠的参数分析与选型3. 磁珠应用中的隐患问题 1. 磁珠的高频等效特性 和磁环类似,低频段感性jwL为主,高频段阻性R为主。 2. 磁珠的参数分析与选型 不需要太在意磁珠在100MHz时的电阻值,选型…

Mediamtx与FFmpeg远程与本地推拉流使用

1.本地推拉流 启服 推流 ffmpeg -re -stream_loop -1 -i ./DJI_0463.MP4 -s 1280x720 -an -c:v h264 -b:v 2000k -maxrate 2500k -minrate 1500k -bufsize 3000k -rtsp_transport tcp -f rtsp rtsp://127.0.0.1:8554/stream 拉流 ffplay -rtsp_transport tcp rtsp://43.136.…

DPIN在AI+DePIN孟买峰会阐述全球GPU生态系统的战略愿景

DPIN基金会在3月29日于印度孟买举行的AIDePIN峰会上展示了其愿景和未来5年的具体发展计划,旨在塑造去中心化算力的未来。本次活动汇集了DPIN、QPIN、社区成员和Web3行业资深顾问,深入探讨DPIN构建全球领先的去中心化GPU算力网络的战略,该网络…

Visual Studio Code 使用tab键往左和往右缩进内容

使用VSCode写东西,经常遇到多行内容同时缩进的情况,今天写文档的时候就碰到,记录下来: 往右缩进 选中多行内容,点tab键,会整体往右缩进: 往左缩进 选中多行内容,按shifttab&am…

HTML、XHTML 和 XML区别

HTML、XHTML 和 XML 这三兄弟的区别 HTML: 老大哥,负责网页长啥样,性格比较随和,有点小错误也能容忍。XHTML: 二哥,看着像 HTML,但规矩严,是按 XML 的规矩来的 HTML,更规范。XML: 小弟,负责存储和传输数据,非常灵活,标签可以自己随便定,但规矩最严。它们仨长啥样?(…

FPGA上实现YOLOv5的一般过程

在FPGA上实现YOLOv5 YOLO算法现在被工业界广泛的应用,虽说现在有很多的NPU供我们使用,但是我们为了自己去实现一个NPU所以在本文中去实现了一个可以在FPGA上运行的YOLOv5。 YOLOv5的开源代码链接为 https://github.com/ultralytics/yolov5 为了在FPGA中…

4U带屏基于DSP/ARM+FPGA+AI的电力故障录波装置设计方案,支持全国产化

4U带屏DSP/ARMFPGAAI电力故障录波分析仪,支持国产化,含有CPU主控模块,96路模拟量采集,256路开关量,通讯扩展卡等#电力故障录波#4U带屏#新能源#电力监测 主要特点 1)是采用嵌入式图形系统,以及…

数据库数据删除与修改实验

数据库数据删除与修改实验 在数据库原理的学习中,数据的删除与修改是核心操作技能。通过“删除修改数据”实验,我系统实践了 SQL 中 UPDATE 和 DELETE 语句的多种应用场景,从基础语法到复杂业务逻辑处理,积累了丰富的实战经验。本…

【含文档+PPT+源码】基于SpringBoot+vue的疫苗接种系统的设计与实现

项目介绍 本课程演示的是一款 基于SpringBootvue的疫苗接种系统的设计与实现,主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的 Java 学习者。 1.包含:项目源码、项目文档、数据库脚本、软件工具等所有资料 2.带你从零开始部署运行本套系…

项目自动化测试

一.设计测试用例(细致全面) 二.先引入所需要的pom.xml依赖 1.selenium依赖 2.webdrivermanager依赖 3.commons-io依赖 编写测试用例–按照页面对用例进行划分,每个页面是Java文件,页面下的所有用例统一管理 三.common包(放入公用包) 类1utils 可以调用driver对象,访问url …

Python爬虫爬取图片并存储到MongoDB(注意:仅尝试存储一条空的示例数据到MongoDB,验证MongoDB的联通性)

以下是一个使用Python爬取图片并存储到MongoDB的示例实现,包含详细步骤说明: import requests from bs4 import BeautifulSoup from pymongo import MongoClient from datetime import datetime import os import re# 配置信息 mongoIP mongodb://root…

L1-1、Prompt 是什么?为什么它能“控制 AI”?

*Prompt 入门 L1-1 想象一下,你只需输入一句话,AI 就能自动为你写一篇文案、生成一份报告、甚至规划你的创业计划。这种“对话即编程”的背后魔法,就是 Prompt 的力量。 🔍 一、Prompt 的定义与由来 Prompt(提示词&am…

TIM输入捕获知识部分

越往左,频率越高;越往右,频率越低。【越紧凑,相同时间,次数越多】 计算频率的方法:测评法、测周法、中界频率。 频率的定义:1s内出现了多少个重复的周期 测评法就是从频率的定义出发的&#…

PCB常见封装类型

1. 电阻、电容、电感封装 2. 二极管、三极管封 3. 排阻类器件(8脚、16脚)封装 4. SO类器件(间距有1.27、2.54mm等)封装 5. QFP类器件封装(四方扁平封装) 结构:引脚分布在封装的四个侧面&#…

【Linux】调试工具gdb的认识和使用指令介绍(图文详解)

目录 1、debug和release的知识 2、gdb的使用和常用指令介绍: (1)、windows下调试的功能: (2)、进入和退出: (3)、调试过程中的相关指令: 3、调试究竟是在…

UML设计系列(9):开发过程中如何应用UML

传送门 UML设计系列(1):状态机图 UML设计系列(2):类图 UML设计系列(3):时序图 UML设计系列(4):用例图 UML设计系列(5):系统依赖图 UML设计系列(6):活动图 UML设计系列(7):UML设计阶段性总…

模板方法模式:定义算法骨架的设计模式

模板方法模式:定义算法骨架的设计模式 一、模式核心:模板方法定义算法骨架,具体步骤延迟到子类实现 在软件开发中,经常会遇到这样的情况:某个算法的步骤是固定的,但具体步骤的实现可能因不同情况而有所不…

通付盾入选苏州市网络和数据安全免费体验目录,引领企业安全能力跃升

近日,苏州市网络安全主管部门正式发布《苏州市网络和数据安全免费体验产品和服务目录》,通付盾凭借其在数据安全、区块链、AI领域的创新实践和前沿技术实力,成功入选该目录。 作为苏州市网络安全技术支撑单位,通付盾将通过 “免费…