AI 内容分享(十三):商品分类:AI落地实践

慈云数据 1年前 (2024-03-15) 技术支持 171 0

目录

AI 内容分享(十三):商品分类:AI落地实践
(图片来源网络,侵删)

背景

思路

AI 内容分享(十三):商品分类:AI落地实践
(图片来源网络,侵删)

实现

1. 准备标准的商品分类

2. 商品目录存入矢量数据库

3. 查询数据

工程化

写在后面


基于真实需求,让AI落地,使用embedding模型做大数据量分类。

为数十万商品分类通常想到的办法是用NLP+特定分类算法(如是SVM)来实现,涉及数据清洗,特征提取,模型训练,调试和集成等工作。看起来是项大工程。 借助现有AI的能力,可以加速实现。本文是基于真实需求场景的探索和回顾。

背景

近期遇到一个做电商的朋友需求,他们的电商平台上有几十万商品,上千种商品品类。而商品品类的划分数据来自多个电商平台,标准描述不统一,分类也有出错的情况,需要对所有商品品类做一个统一的梳理。梳理商品品类的工作由人工完成的话,会很耗时费力。期望借助AI的能力帮忙梳理已有商品品类的划分,而且对于新加入的商品,能自动为其分类。

传统的关键字匹配不合适,比如葡萄,葡萄干,葡萄糖,葡萄石就是四种品类。传统的NLP处理也有局限性,且需要重新训练。

另外,还有限制条件:

  • 商品名主要是中文

  • 只能在内网使用

  • 没有性能强大(更别提GPU)的服务器

    思路

    首先想到的是微调一个4bit量化的中文LLM,来实现输入商品名,返回商品二级和一级分类。

    已知:

    • ChatGLM3 4bit模型在一般的CPU服务器,16GB内存情况下是能跑起来。

    • 需要准备500到1000条高质量,覆盖面广的训练数据。

    • 需要调教和控制输出格式。

      实测下来,4bit LLM能力有限,输出的准确度和格式的一致性不能保证。需要多次“炼丹”,结果还不能保证达到想要的效果。

      换个思路,我们要解决传统关键字匹配的问题,本质上是语义的匹配。在以前做知识库问答的过程中用到的embeddings不就是实现了语义的匹配吗?前述的商品分类需求中,并不涉及语言理解和逻辑推理,那其实可以不用LLM。是不是只需要embedding模型就能实现了?

      嵌入式模型(Embedding)是一种广泛应用于自然语言处理(NLP)和计算机视觉(CV)等领域的机器学习模型,它可以将高维度的数据转化为低维度的嵌入空间(embedding space),并保留原始数据的特征和语义信息,从而提高模型的效率和准确性

      说人话,嵌入式模型就是把词或句用多维向量来表示,向量之间的距离表示语义的相近程度。向量之间的距离越短,表示语义越接近。比如“土豆”->[0,1,2],“马铃薯”->[0,1,1],“土狗”->[1,0,0]。比较[0,1,2]与[0,1,1]的距离要小于[0,1,2]与[1,0,0]的距离,得出结论“土豆”表达的意思与“马铃薯”更接近。

      上面的例子中只是一个3维向量,而实际可用的嵌入式模型中,维度要大得多,比如OpenAI提供的Embeddings接口支持1536维度,开源的中文embeddings中m3e-base支持768维度。

      所以,我们需要以下服务:

      • embedding模型,这里选m3e-base

      • 本地向量数据库,选Milvus

      • 本地关系数据库,  选MySQL

        embedding 模型, 矢量数据库和关系数据库有许多其它可选的,这里不展开讨论选型了哈。

        实现

        1. 准备标准的商品分类

        商品分类的元数据可以从已有的商品分类中提炼,也可借助于AI生成新的。

        由于AI推理的一定局限性和输出有token限制,我们不要一次性让它生成所有的商品分类。采取分步策略,可以获得更好的结果。

        先生成一级目录,再一个个生成二级目录,再生成对应的三级目录。这个过程循环操作并记录,理论上就能得到一个全新的,覆盖面较全的商品分类元数据了。

        这样操作有个弊端,对话轮次太多了,得有几百次。我们可以用工程化的方法来操作,也就是程序调用API。用Shell或Python都可以,可以让ChatGPT帮忙写这脚本。

        # 第一步:生成二级目录
        def generate_second_level_categories(first_level_category):
            # 生成二级目录的 prompt
            prompt = f"一级目录: {first_level_category};"
            # 调用 OpenAI API 生成二级目录
            response = call_openai_api(prompt)
            # 解析二级目录
            second_level_categories = response.split(",")
            print(f"一级目录: {first_level_category}; 二级目录: {second_level_categories}")
            return second_level_categories
        # 第二步:生成三级目录
        def generate_third_level_categories(first_level_category, second_level_category):
            # 生成三级目录的 prompt
            prompt = f"一级目录: {first_level_category}; 二级目录: {second_level_category};"
            # 调用 OpenAI API 生成三级目录
            response = call_openai_api(prompt)
            # 解析三级目录
            third_level_categories = response.split(",")
            print(f"一级目录: {first_level_category}; 二级目录: {second_level_category}; 三级目录: {third_level_categories}")
            return third_level_categories
        # 保存结果
        with open("categories.csv", "w", newline="") as csvfile:
            writer = csv.writer(csvfile)
            for first_level_category, second_level_category, third_level_category in third_level_categories.items():
                writer.writerow([third_level_category, second_level_category, first_level_category])
        

        生成的商品类目并不是我想要的TSV格式,但只要有格式,就好解析。再让AI写个脚本来把这些数据加载到MySQL。至此,商品分类元数据准备完毕。

        2. 商品目录存入矢量数据库

        我们使用 m3e-base 模型来做中文embedding,也就是将三级商品名转换成向量。然后选用Milvus来做矢量数据库,储存转换好的向量。另外,还需要在MySQL中储存向量Index与对应的三级商品名。

        2.1 me3-base 安装使用me3-base的安装参考 https://huggingface.co/moka-ai/m3e-basepip install -U sentence-transformers

        初次运行时,会联网下载Model,耗时。如果需要,可向我索取离线包。 准备text_to_vector方法如下。

        from sentence_transformers import SentenceTransformer
        # Initialize the model
        model = SentenceTransformer('moka-ai/m3e-base')
        def text_to_vector(text):
            embedding = model.encode([text])
            return embedding[0]
        

        2.2 Milvs 安装使用最方便的方式就是用Docker来启动Milvus服务, 参考 https://milvus.io/docs/install_standalone-docker.md

        wget https://github.com/milvus-io/milvus/releases/download/v2.3.3/milvus-standalone-Docker-Compose.yml -O docker-compose.yml
        sudo docker-compose up -d

        矢量数据库Milvus准备好后,就可以将text_to_vector转换后的矢量存进去,然后创建index, 把index对应的text也记得存入MySQL。

        pip install pyarrow pymysql pymilvus milvus

        def save_embeddings():
            # Establish connections
            connections, db = establish_connections()
            try:
                collection_name = "text_search_collection"
                if utility.has_collection(collection_name):
                    collection = Collection(collection_name)
                    collection.drop()
                fields = [
                    FieldSchema(name="text_id", dtype=DataType.INT64, is_primary=True, auto_id=True),
                    FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=768)
                ]
                schema = CollectionSchema(fields, description="Text search collection")
                collection = Collection(collection_name, schema)
                texts = ["干果", "水果", "宝石", "医疗用品", "零食"]
                vectors = [text_to_vector(text) for text in texts]
                mr = collection.insert([vectors])
                ids = mr.primary_keys
                index_params = {
                    "metric_type": "L2",
                    "index_type": "IVF_FLAT",
                    "params": {"nlist": 128}
                }
                print("create vector index")
                collection.create_index("embedding", index_params)
                data = [(vector_id, text) for text, vector_id in zip(texts, ids)]
                cursor = db.cursor()
                try:  
                    cursor.executemany('INSERT INTO text_table (text_id, text) VALUES (%s, %s)', data)
                    db.commit()
                finally:
                    cursor.close()
            finally:
                close_connections(connections, db)
        

        3. 查询数据库

        查询的过程看上面的图:文本先通过text_to_vector转换为矢量,然后在Milvus里查询最邻近的值,拿出Index,再根据这个Index到MySQL里查询对应的文本。

        以下是查询方法

        def search_similar_texts(query_text):
            connections, db = establish_connections()
            try:
                query_vector = text_to_vector(query_text)
                collection = Collection("text_search_collection")
                collection.load()
                search_params = {"metric_type": "L2", "params": {"nprobe": 16}}
                results = collection.search([query_vector], "embedding", search_params, limit=1)
                ids = [str(result.id) for result in results[0]]
                distances = [str(result.distance) for result in results[0]]
                cursor = db.cursor()
                try:
                    query = f"SELECT text FROM text_table WHERE text_id IN ({','.join(['%s'] * len(ids))})"
                    cursor.execute(query, tuple(ids))
                    similar_texts = [text for text, in cursor.fetchall()]
                finally:
                    cursor.close()
            finally:
                close_connections(connections, db)
            return similar_texts, ids, distances
        

        上面的代码中有distances值,表示矢量数据库查询计算出的两个矢量的距离,距离越小,表示语义最接近。所以, 我们也可以将distances值返回,用于人工审核的阈值筛选条件。比如dinstances值大于某个值的,我们要人工审核一下。

        查询:

        search_words = ["葡萄", "葡萄干", "葡萄糖", "葡萄石"]
        for search_word in search_words:
            print(f" {search_word} 属于分类:",search_similar_texts(search_word))
        

        结果:

         葡萄 属于分类: (['水果'], ['445652651623587475'], ['156.342529296875'])
         葡萄干 属于分类: (['干果'], ['445652651623587474'], ['91.40153503417969'])
         葡萄糖 属于分类: (['零食'], ['445652651623587478'], ['176.91494750976562'])
         葡萄石 属于分类: (['宝石'], ['445652651623587476'], ['123.83802032470703'])
        

        工程化

        环境安装好,分步调试好,就可以组装了。接下来可能要完善的包括

        • 商品分类元数据的更新

        • 查询邻近分类的API接口

        • 人工审核步骤(可选)

        • 集成到已有系统

          写在后面

          对于特定场景问题,不一定非得上最优的大模型,只用embedding模型就能解决。AI应用落地的关键点在落地。只要能解决问题,方案应该尽可能简洁,便宜。

          本文记录了使用语言embedding模型和矢量数据库做商品分类查询的实现方式。相比于之前讲过的知识库问答,少了大语言模型(LLM)的使用。为了方便使用,我也准备了离线安装包。可加我vx yahai00,索取embedding分类离线安装包。

           

微信扫一扫加客服

微信扫一扫加客服