引言: 从搜索困境到语义理解
不知道大家有没有这样的经历:在百度搜索时,因为关键词没选对而错过大量有用信息?或者在预订酒店时,明明描述很符合需求,却因为用词不同而被搜索引擎忽略?
这就是传统的布尔逻辑和关键词匹配搜索已显现出显著的局限性。传统的搜索引擎往往依赖于精确的字词匹配,就像只会认字不会理解的伪人同事,难以捕捉查询背后的真实意图。传统的数据库只能进行精确匹配,而人类的大脑却是通过语义和关联来存储信息的。当我们想到"海滩度假",脑中浮现的是阳光、沙滩、泳池、放松等一系列相关概念,而不是死板的关键词匹配。“向量嵌入Embedding”正是为了解决这个问题而生;旨在将文本、图像、音频等非结构化数据转换为低维、稠密的分布式向量表示。将数据映射到语义空间中,语义相似的数据点在几何距离上也相近。
今天,我们要探讨的Embedding和向量数据库,正是构建AI「语义大脑」的核心技术。通篇报告每小节都有案例辅助理解,大家可以自己试着跑一跑。数据来源:
之前的公众号文章当中有提到过Embedding,但是我感觉我说的不太专业,所以我今天重新梳理并添加向量数据库的内容,也同时为下一篇RAG实战入门做一下知识点补充。
一、 从精确匹配到语义理解:为什么需要Embedding?
举个例子,在国庆放假想要出去玩,那在庞大的酒店和民宿数据中寻找「适合家庭入住的舒适酒店」。传统的搜索引擎可能会匹配包含「家庭」、「舒适」等关键词的结果,但如果某家酒店的描述是「亲子友好型住宿,提供温馨的居住体验」,即使它完美符合需求,也可能因为关键词不匹配而被遗漏。——缺乏语义理解能力。
图1:西雅图152家酒店名称及描述
1.1 余弦相似度:衡量语义距离的尺子
从底层的逻辑来分析,根据人的指令去推荐合适的酒店是一个寻找similarity的过程。总所周知,similarity可以通过余弦去计算,通过计算两个向量夹角的余弦值来衡量它们的相似程度。在推荐系统中,我们将酒店的文本描述转换为数值向量,然后通过余弦相似度找到最匹配的酒店。几何意义上,两个向量夹角越小,其余弦值越接近 1,表示它们的方向越相似,语义上也越接近;当夹角为90°时,余弦值为 0,表示不相关,方向完全相反时,余弦相似度的值为-1。
而大模型在收到一堆文字信息的时候如何把他转换成数字进行计算的,这就出现了token。我举个例子:
句子A:“这个程序代码太乱,那个代码规范”
句子A变成:”这个/程序/代码/太乱,那个/代码/规范“
句子B:“这个程序代码不规范,那个更规范”
句子B变成:”这个/程序/代码/不/规范,那个/更/规范“
这里就可以计算出现频次了:“这个1,程序1,代码2,太乱1,那个1,规范1,不0,更0” 这样之后就可以通过以上的公式进行计算了,结果我表明是0.866,所以说明这两个句子是相似的。
import numpy as npfrom sklearn.feature_extraction.text import CountVectorizerfrom sklearn.metrics.pairwise import cosine_similarityimport jieba# 定义两个句子sentence_A = "这个程序代码太乱,那个代码规范"sentence_B = "这个程序代码不规范,那个代码规范"print("句子A:", sentence_A)print("句子B:", sentence_B)print()# 使用jieba进行中文分词tokens_A = ' '.join(jieba.cut(sentence_A))tokens_B = ' '.join(jieba.cut(sentence_B))print("分词后:")print("句子A:", tokens_A)print("句子B:", tokens_B)print()# 使用CountVectorizer构建词频向量vectorizer = CountVectorizer()corpus = [tokens_A, tokens_B]X = vectorizer.fit_transform(corpus)# 获取词汇表feature_names = vectorizer.get_feature_names_out()print("词汇表:", list(feature_names))print()# 获取向量vector_A = X[0].toarray()[0]vector_B = X[1].toarray()[0]print("句子A的向量:", vector_A)print("句子B的向量:", vector_B)print()# 计算余弦相似度similarity = cosine_similarity(X[0:1], X[1:2])[0][0]print("="*50)print(f"余弦相似度: {similarity:.3f}")print("="*50)二、从词频统计到语义理解的技术演进
大模型接收到文字信息时,首先需要将其转换为数字进行计算,这个过程就是tokenization(分词)。 大家有没有感觉这个有一点似曾相识的感觉!对没错就是N-Gram的基本概念。(具体的可以去之前的链接里面看)它基于一个马尔可夫假设:第 n 个词的出现仅与前 n−1 个词相关。N-Gram 将文本中的 N 个连续 item 序列作为一个特征单元,例如,文本 “ABCDE” 的 Bi-Gram(2 元)特征为 AB, BC, CD, DE。在特征工程中,N-Gram 常用于捕捉局部上下文信息,例如在酒店描述中提取 Bi-Gram “pike place” 或 Tri-Gram “pike place market” 作为新的特征集合。然而,N-Gram 这类稀疏编码方式在大型语料库中仍会导致特征矩阵维度极高且绝大部分为零,计算量庞大。不可避免的一些常见的词汇出现频率会很高,比如酒店案例中的“i”“me”“and”等词汇,这正是推动文本表示向稠密 Embedding 演进的核心驱动因素。
import pandas as pdfrom sklearn.feature_extraction.text import CountVectorizerimport matplotlib.pyplot as pltimport warningswarnings.filterwarnings('ignore')# 支持中文plt.rcParams['font.sans-serif'] = ['SimHei']plt.rcParams['axes.unicode_minus'] = False# 数据加载df = pd.read_csv('hotel_recommendation/Seattle_Hotels.csv', encoding="latin-1")print('数据集中的酒店个数:', len(df))# 创建英文停用词列表ENGLISH_STOPWORDS = {'i', 'me', 'my', 'myself', 'we', 'our', 'ours', 'ourselves', 'you', "you're", "you've", "you'll", "you'd", 'your', 'yours', 'yourself', 'yourselves', 'he', 'him', 'his', 'himself', 'she', "she's", 'her', 'hers', 'herself', 'it', "it's", 'its', 'itself', 'they', 'them', 'their', 'theirs', 'themselves', 'what', 'which', 'who', 'whom', 'this', 'that', "that'll", 'these', 'those', 'am', 'is', 'are', 'was', 'were', 'be', 'been', 'being', 'have', 'has', 'had', 'having', 'do', 'does', 'did', 'doing', 'a', 'an', 'the', 'and', 'but', 'if', 'or', 'because', 'as', 'until', 'while', 'of', 'at', 'by', 'for', 'with', 'about', 'against', 'between', 'into', 'through', 'during', 'before', 'after', 'above', 'below', 'to', 'from', 'up', 'down', 'in', 'out', 'on', 'off', 'over', 'under', 'again', 'further', 'then', 'once', 'here', 'there', 'when', 'where', 'why', 'how', 'all', 'any', 'both', 'each', 'few', 'more', 'most', 'other', 'some', 'such', 'no', 'nor', 'not', 'only', 'own', 'same', 'so', 'than', 'too', 'very', 's', 't', 'can', 'will', 'just', 'don', "don't", 'should', "should've", 'now', 'd', 'll', 'm', 'o', 're', 've', 'y', 'ain', 'aren', "aren't", 'couldn', "couldn't", 'didn', "didn't", 'doesn', "doesn't", 'hadn', "hadn't", 'hasn', "hasn't", 'haven', "haven't", 'isn', "isn't", 'ma', 'mightn', "mightn't", 'mustn', "mustn't", 'needn', "needn't", 'shan', "shan't", 'shouldn', "shouldn't", 'wasn', "wasn't", 'weren', "weren't", 'won', "won't", 'wouldn', "wouldn't"}defprint_description(index): example = df[df.index == index][['desc', 'name']].values[0]if len(example) > 0: print(example[0]) print('Name:', example[1])print('第10个酒店的描述:')print_description(10)# 得到酒店描述中n-gram特征中的TopK个defget_top_n_words(corpus, n=1, k=None):# 统计ngram词频矩阵,使用自定义停用词列表 vec = CountVectorizer(ngram_range=(n, n), stop_words=list(ENGLISH_STOPWORDS)).fit(corpus) bag_of_words = vec.transform(corpus) sum_words = bag_of_words.sum(axis=0) words_freq = [(word, sum_words[0, idx]) for word, idx in vec.vocabulary_.items()]# 按照词频从大到小排序 words_freq =sorted(words_freq, key = lambda x: x[1], reverse=True)return words_freq[:k]common_words = get_top_n_words(df['desc'], n=3, k=20)df1 = pd.DataFrame(common_words, columns = ['desc' , 'count'])df1.groupby('desc').sum()['count'].sort_values().plot(kind='barh', title='去掉停用词后,酒店描述中的Top20单词')plt.show()2.1TF-IDF:频率与权重
计算词频需要引入一个新的概念,TF-IDF是一种统计方法,因为单纯的统计词频不够只能,他结合词频(TF, Term Frequency)和逆向文档频率(IDF, Inverse Document Frequency)来衡量一个词在文档中的重要性和区分度。词频与词语的重要性呈正比,而 IDF 则衡量了词语的区分度:一个词在越少的文档中出现,其 IDF 越大,区分度也越大。在酒店推荐系统等场景中,TF-IDF 可用于对酒店描述(Desc)进行特征提取,然后基于生成的 TF-IDF 矩阵计算酒店间的余弦相似度进行推荐。尽管 TF-IDF 改善了纯词频方法的不足,但它仍基于词袋模型(Bag-of-Words),丢失了词序信息,且无法捕捉深层语义关系(例如,它不能识别“好”和“优秀”在语义上是接近的),生成的特征矩阵仍然是稀疏且计算资源消耗大的。
例子:使用以上的酒店的数据,得到结果如下:
152个酒店的特征在使用N-Garm和TFIDF后的特征矩阵有3347个,数量庞大难以计算。这种方法的核心在于将文本转化为一个庞大的“词袋”,并基于词语的统计特征来评估其重要性。具体而言,TF-IDF通过评估一个词语在单个文档中出现频率和在所有文档中出现频率的对比,来判断该词语对于该文档的代表性。N-Grams技术则进一步考虑了词语的局部顺序关系,通过提取连续的N个词序列作为特征,从而能够捕捉到像“无边泳池”或“儿童乐园”这类符合短语的特定含义,这远比“泳池”“儿童”更能精确描述酒店的特征。 但是这种方法会带来一个显著的工程任务:特征矩阵的维度爆炸与稀疏性。这还只是152家酒店,如果处理成千上万的酒店描述,并考虑1-gram、2-gram甚至3-gram时,特征空间轻松可以达到数万甚至数十万维,计算和村春成本呈指数级增长。每一个酒店都被表示成一个极高维度的向量,其中绝大多数位置的值是零,因为任何一个具体的描述都只包含所有可能词汇组合中极小的一部分。这种稀疏性不仅对计算资源和存储空间提出了严峻考验,更致命的是,它从根本上无法捕捉语言的深层语义。例如,基于TF-IDF的模型会认为“酒店很舒适”和“这家住宿非常惬意”毫无关联,因为它们的用词完全不同;它也无法理解“北京”和“首都”之间的紧密联系。甚至可能会忽略词序,在他看来“狗咬人”和“人咬狗”是一样的。这种“词汇鸿沟”、“语义缺失”和“词序忽略”的问题极大地限制了推荐系统的精准度和智能化水平。正是为了突破这一瓶颈,词嵌入技术应运而生,其代表就是GloVe和Word2Vec。
import pandas as pdfrom sklearn.feature_extraction.text import TfidfVectorizerfrom sklearn.metrics.pairwise import linear_kernelimport reimport warningswarnings.filterwarnings('ignore')# 数据加载df = pd.read_csv('hotel_recommendation/Seattle_Hotels.csv', encoding="latin-1")print('数据集中的酒店个数:', len(df))# 创建英文停用词列表ENGLISH_STOPWORDS = {'i', 'me', 'my', 'myself', 'we', 'our', 'ours', 'ourselves', 'you', "you're", "you've", "you'll", "you'd", 'your', 'yours', 'yourself', 'yourselves', 'he', 'him', 'his', 'himself', 'she', "she's", 'her', 'hers', 'herself', 'it', "it's", 'its', 'itself', 'they', 'them', 'their', 'theirs', 'themselves', 'what', 'which', 'who', 'whom', 'this', 'that', "that'll", 'these', 'those', 'am', 'is', 'are', 'was', 'were', 'be', 'been', 'being', 'have', 'has', 'had', 'having', 'do', 'does', 'did', 'doing', 'a', 'an', 'the', 'and', 'but', 'if', 'or', 'because', 'as', 'until', 'while', 'of', 'at', 'by', 'for', 'with', 'about', 'against', 'between', 'into', 'through', 'during', 'before', 'after', 'above', 'below', 'to', 'from', 'up', 'down', 'in', 'out', 'on', 'off', 'over', 'under', 'again', 'further', 'then', 'once', 'here', 'there', 'when', 'where', 'why', 'how', 'all', 'any', 'both', 'each', 'few', 'more', 'most', 'other', 'some', 'such', 'no', 'nor', 'not', 'only', 'own', 'same', 'so', 'than', 'too', 'very', 's', 't', 'can', 'will', 'just', 'don', "don't", 'should', "should've", 'now', 'd', 'll', 'm', 'o', 're', 've', 'y', 'ain', 'aren', "aren't", 'couldn', "couldn't", 'didn', "didn't", 'doesn', "doesn't", 'hadn', "hadn't", 'hasn', "hasn't", 'haven', "haven't", 'isn', "isn't", 'ma', 'mightn', "mightn't", 'mustn', "mustn't", 'needn', "needn't", 'shan', "shan't", 'shouldn', "shouldn't", 'wasn', "wasn't", 'weren', "weren't", 'won', "won't", 'wouldn', "wouldn't"}# 文本预处理REPLACE_BY_SPACE_RE = re.compile('[/(){}\[\]\|@,;]')BAD_SYMBOLS_RE = re.compile('[^0-9a-z #+_]')STOPWORDS = ENGLISH_STOPWORDS# 对文本进行清洗defclean_text(text):# 全部小写 text = text.lower()# 用空格替代一些特殊符号,如标点 text = REPLACE_BY_SPACE_RE.sub(' ', text)# 移除BAD_SYMBOLS_RE text = BAD_SYMBOLS_RE.sub('', text)# 从文本中去掉停用词 text = ' '.join(word for word in text.split() if word notin STOPWORDS) return text# 对desc字段进行清理,apply针对某列df['desc_clean'] = df['desc'].apply(clean_text)# 建模df.set_index('name', inplace = True)# 使用TF-IDF提取文本特征,使用自定义停用词列表tf = TfidfVectorizer(analyzer='word', ngram_range=(1, 3), min_df=0.01, stop_words=list(ENGLISH_STOPWORDS))# 针对desc_clean提取tfidftfidf_matrix = tf.fit_transform(df['desc_clean'])print('TFIDF feature names:')print(len(tf.get_feature_names_out()))# 计算酒店之间的余弦相似度(线性核函数)cosine_similarities = linear_kernel(tfidf_matrix, tfidf_matrix)print('相似度矩阵形状:', cosine_similarities.shape)# 显示关键信息print("\n" + "="*70)print(f"152个酒店 → {len(tf.get_feature_names_out())} 维特征矩阵")print(f"矩阵形状: {tfidf_matrix.shape}")print("="*70)三、 Word2Vec深度解析:分布式表示的基石
3.1 从稀疏到稠密:分布式表示的突破
Word2Vec 是由谷歌 Tomas Mikolov 领导的研究团队于 2013 年创建、申请专利并发布。它将文本表示从稀疏转向稠密的里程碑式模型。其核心目标是通过无监督学习,将词语从高维稀疏空间映射到低维(例如 100 维)、稠密的向量空间。这种映射使得在语义上相似的词语,在新的向量空间中距离也相近。这个低维向量的维度,通常被称为 hidden_size 或 Embedding Size。 它们的核心思想是一场范式革命:放弃庞大的稀疏向量,转而学习一个低维、稠密的连续向量空间来表征每一个词语。在这个空间中,每一个词都被映射为一个固定长度(例如50维、100维或300维)的实数向量,向量的每一个维度不再对应一个具体的词语或短语,而是捕获了某种潜在的、抽象的语义或语法特征。神奇之处在于,在这个稠密向量空间中,语义相似的词语,比如“酒店”和“宾馆”,会自然而然地聚集在相近的位置。 最著名的例子就是将king - man + woman ≈ queen 在一个假设的世界中,向量可以定义词汇表中每个给定单词的每个标准(例如king可以理解为“皇室、男性、权力”那以上这个例子就跟人类理解的其实差不多了)。
from gensim.models import word2vecimport warningswarnings.filterwarnings('ignore')print("="*70)print("Word2Vec向量运算演示: king - man + woman = queen")print("="*70)try:# 尝试使用gensim-data加载预训练模型import gensim.downloader as api print("\n正在加载预训练模型...") print("(首次运行会自动下载,请耐心等待)")# 使用较小的glove模型进行演示 model = api.load("glove-wiki-gigaword-100") print("✓ 模型加载成功!\n")# 测试词汇相似度 print("词汇相似度测试:") print("-"*70) test_pairs = [ ('king', 'queen'), ('man', 'woman'), ('king', 'man'), ]for word1, word2 in test_pairs:try: similarity = model.similarity(word1, word2) print(f"{word1} <-> {word2}: {similarity:.4f}")except KeyError: print(f"{word1} <-> {word2}: 词不在词汇表中")# 向量运算:king - man + woman = queen print("\n" + "="*70) print("类比推理: king - man + woman = ?") print("="*70) result = model.most_similar(positive=['king', 'woman'], negative=['man'], topn=5) print("\n结果:")for i, (word, score) in enumerate(result, 1): print(f" {i}. {word}: {score:.4f}")if result[0][0].lower() == 'queen': print("\n✓ 成功!第一个结果是 'queen'")# 更多类比推理示例 print("\n" + "="*70) print("更多类比推理示例") print("="*70) analogies = [ (['paris', 'germany'], ['france'], 'paris - france + germany = ?'), (['good', 'better'], ['bad'], 'good - bad + better = ?'), ]for positive, negative, desc in analogies:try: print(f"\n{desc}") result = model.most_similar(positive=positive, negative=negative, topn=3)for word, score in result: print(f" {word}: {score:.4f}")except KeyError: print(f" 部分词汇不在模型中")except Exception as e: print(f"\n无法加载预训练模型: {e}") print("\n提示: 需要安装gensim并下载预训练模型") print("运行: pip install gensim")3.2 Word2Vec 的神经网络原理
Word2Vec是从无到有的可以理解人类语义是因为其内部的神经网络机制,【之前也有相关的文章,感兴趣可以点击查看,本来偷懒没详细写Word2Vec的内容,现在跑不了了】Word2Vec的目标并非直接完成分类或翻译等复杂任务,它的设计初衷非常纯粹:学习一个能很好预测词语上下文的模型,而其副产品——神经网络的权重矩阵——就是我们最终需要的词向量表。它主要通过两种模式来实现这一目标:CBOW(连续词袋模型)和Skip-gram(跳字模型)。CBOW试图通过上下文词语来预测中心词,而Skip-gram则相反,它通过中心词来预测其周围的上下文词语。尽管任务方向相反,但它们的网络结构本质是相通的。简单来理解就是通过Embedding,把原先词所在空间映射到一个新的空间中去,使得语义上相似的单词在该空间内距离相近。
让我们以Skip-gram模型为例,深入其神经网络内部一探究竟。假设我们的词汇表大小为V(例如1万个词),我们期望的词向量维度为N(例如300维)。那么,这个简单的神经网络仅包含三层:输入层、单个隐藏层和输出层。
- 输入层与投影:输入是一个采用One-Hot编码的中心词。这是一个长度为V的向量,只有在对应词语索引的位置为1,其余全为0。这个向量与一个大小为V × N的权重矩阵 W 相乘。由于One-Hot编码的特性,这个矩阵乘法实际上只是从矩阵 W 中“查找”并提取出对应于输入词的那一行。这一行、这个N维的向量,就是该输入词的词向量。因此,这个权重矩阵 W 也被称为“词嵌入查找表”。
- 隐藏层:隐藏层没有使用任何激活函数,它直接接收从输入层投影而来的N维词向量。你可以将这个隐藏层视为词向量的“暂存区”或“语义表示层”。
- 输出层:隐藏层的输出向量会与另一个大小为N × V的权重矩阵 W’ 相乘,得到一个V维的向量。随后,通过Softmax函数将这个V维向量转换为一个概率分布,其中每个位置的值代表词汇表中每个词语作为中心词上下文词语的概率。
整个模型的训练过程,就是通过大量文本数据,不断调整这两个权重矩阵 W 和 W’ 的值,使得当输入是“海滩”时,模型预测出“沙滩”、“阳光”、“度假”等词语的概率尽可能高。训练完成后,我们通常丢弃输出层,而将输入层的权重矩阵 W作为最终的词向量集合。因为此时,W已经成功地被训练成一个能够将语义和语法关系编码进向量空间的高质量词嵌入表。
3.3 Word2Vec应用
理解了Word2Vec的精妙原理后,在实际应用中,我们并不需要每次都从零开始训练。强大的工具Gensim库已经为我们封装好了这一切,使Word2Vec的使用变得异常便捷。我是导入西游记和三国演义的文章,直接让他读取并学习的。
- 《三国演义》分词处理
# 对《三国演义》txt文件进行中文分词import jiebaimport osfrom word2vec.utils import files_processing# 源文件所在目录source_folder = './word2vec/three_kingdoms/source'segment_folder = './word2vec/three_kingdoms/segment'# 创建segment目录(如果不存在)ifnot os.path.exists(segment_folder): os.makedirs(segment_folder)# 字词分割,对整个文件内容进行字词分割defsegment_lines(file_list, segment_out_dir, stopwords=[]):""" 对文件列表进行分词处理 :param file_list: 文件路径列表 :param segment_out_dir: 分词结果输出目录 :param stopwords: 停用词列表 """for i, file in enumerate(file_list): segment_out_name = os.path.join(segment_out_dir, 'segment_{}.txt'.format(i)) print(f"正在处理文件: {file}")with open(file, 'r', encoding='utf-8', errors='ignore') as f: document = f.read() document_cut = jieba.cut(document) sentence_segment = []for word in document_cut:# 过滤停用词和空字符,只保留有意义的中文词汇if (word notin stopwords and word.strip() and len(word) > 1and# 过滤单字not word.isdigit() and# 过滤纯数字 word notin [',', '。', '!', '?', ';', ':', '、', '(', ')', '《', '》', '"', '"', ''', ''', ' ', '\n', '\t', '\r']): sentence_segment.append(word) result = ' '.join(sentence_segment)with open(segment_out_name, 'w', encoding='utf-8') as f2: f2.write(result) print(f"分词完成,输出文件: {segment_out_name}")# 对source中的txt文件进行分词,输出到segment目录中if __name__ == "__main__": file_list = files_processing.get_files_list(source_folder, postfix='*.txt') print(f"找到 {len(file_list)} 个文件需要处理")# 定义停用词(可以根据需要添加更多) stopwords = ['的', '了', '在', '是', '我', '有', '和', '就', '不', '人', '都', '一', '个', '上', '也', '很', '到', '说', '要', '去', '你', '会', '着', '没有', '看', '好', '自己', '这'] segment_lines(file_list, segment_folder, stopwords) print("《三国演义》分词处理完成!")- 《三国演义》Word2Vec训练
# 对《三国演义》进行Word2Vec训练和相似度计算from gensim.models import word2vecimport multiprocessingimport os# 分词文件目录segment_folder = './word2vec/three_kingdoms/segment'model_folder = './word2vec/three_kingdoms/models'# 创建模型保存目录ifnot os.path.exists(model_folder): os.makedirs(model_folder)print("=== 《三国演义》Word2Vec训练 ===")print()# 如果目录中有多个文件,可以使用PathLineSentencessentences = word2vec.PathLineSentences(segment_folder)print(f"加载分词数据,文件目录: {segment_folder}")# 设置模型参数,进行训练print("开始训练Word2Vec模型...")model = word2vec.Word2Vec( sentences, vector_size=100, # 词向量维度 window=5, # 窗口大小 min_count=5, # 最小词频 workers=multiprocessing.cpu_count(), # 使用多核 epochs=10, # 训练轮数 sg=0, # 使用CBOW模型 hs=0, # 使用负采样 negative=5# 负采样数量)# 保存模型model_path = os.path.join(model_folder, 'three_kingdoms_word2vec.model')model.save(model_path)print(f"模型已保存到: {model_path}")print()# 测试词汇相似度print("=== 词汇相似度测试 ===")test_pairs = [ ('曹操', '刘备'), ('曹操', '孙权'), ('刘备', '关羽'), ('刘备', '张飞'), ('关羽', '张飞'), ('诸葛亮', '周瑜'), ('吕布', '赵云'), ('曹操', '袁绍'), ('刘备', '刘表'), ('孙权', '孙策')]for word1, word2 in test_pairs:try: similarity = model.wv.similarity(word1, word2) print(f"{word1} vs {word2}: {similarity:.4f}")except KeyError as e: print(f"{word1} vs {word2}: 词汇不在模型中")print()# 测试最相似的词汇print("=== 最相似词汇测试 ===")test_words = ['曹操', '刘备', '关羽', '张飞', '诸葛亮', '周瑜', '吕布', '赵云']for word in test_words:try: print(f"与'{word}'最相似的词汇:") most_similar = model.wv.most_similar(word, topn=10)for similar_word, score in most_similar: print(f" {similar_word}: {score:.4f}") print()except KeyError: print(f"'{word}'不在模型中") print()# 测试词汇类比print("=== 词汇类比测试 ===")analogies = [ (['刘备', '关羽'], ['张飞'], '刘备 + 关羽 - 张飞 ≈ ?'), (['曹操', '刘备'], ['孙权'], '曹操 + 刘备 - 孙权 ≈ ?'), (['关羽', '张飞'], ['赵云'], '关羽 + 张飞 - 赵云 ≈ ?'), (['诸葛亮', '刘备'], ['周瑜'], '诸葛亮 + 刘备 - 周瑜 ≈ ?')]for positive, negative, description in analogies:try: result = model.wv.most_similar(positive=positive, negative=negative, topn=5) print(f"{description}")for word, score in result: print(f" {word}: {score:.4f}") print()except KeyError as e: print(f"{description}: 部分词汇不在模型中") print()print("=== 训练和测试完成 ===")最后得到结果 曹操+刘备-张飞约等于曹操(刘备也行)。
四、 从词向量到智能推荐系统
4.1 构建语义感知的酒店推荐
现在回到最初的酒店推荐问题。采用Word2Vec后,整个系统实现了质的飞跃:
classSemanticHotelRecommender: def__init__(self, word2vec_model): self.model = word2vec_model self.hotels = [] self.hotel_vectors = []defadd_hotel(self, name, description):"""添加酒店并生成语义向量"""# 分词 words = list(jieba.cut(description))# 生成词向量并平均得到文档向量 word_vectors = []for word in words:if word in self.model.wv: word_vectors.append(self.model.wv[word])if word_vectors: hotel_vector = np.mean(word_vectors, axis=0)else: hotel_vector = np.zeros(self.model.vector_size) self.hotels.append({'name': name, 'description': description}) self.hotel_vectors.append(hotel_vector)defrecommend(self, query, top_n=3):"""基于语义的酒店推荐"""# 生成查询的语义向量 query_words = list(jieba.cut(query)) query_vectors = []for word in query_words:if word in self.model.wv: query_vectors.append(self.model.wv[word])ifnot query_vectors:return [] query_vector = np.mean(query_vectors, axis=0)# 计算与所有酒店的余弦相似度 similarities = []for i, hotel_vector in enumerate(self.hotel_vectors): similarity = np.dot(query_vector, hotel_vector) / ( np.linalg.norm(query_vector) * np.linalg.norm(hotel_vector)) similarities.append((i, similarity))# 按相似度排序 similarities.sort(key=lambda x: x[1], reverse=True)# 返回Top-N推荐 recommendations = []for idx, similarity in similarities[:top_n]: hotel = self.hotels[idx].copy() hotel['semantic_similarity'] = similarity recommendations.append(hotel)return recommendations# 使用示例defdemo_semantic_recommendation():# 初始化推荐器 recommender = SemanticHotelRecommender(model)# 添加酒店数据 hotels_data = [ ("海滨度假村", "豪华海滨度假村 拥有私人沙滩和无边泳池 适合家庭度假"), ("商务酒店", "商务型酒店 位于市中心 交通便利 适合商务出行"), ("亲子酒店", "亲子主题酒店 拥有儿童乐园和亲子活动 适合家庭游客"), ("经济酒店", "经济型连锁酒店 价格实惠 干净整洁 适合预算有限的旅客") ]for name, desc in hotels_data: recommender.add_hotel(name, desc)# 用户查询 user_queries = ["想要一个适合带孩子住的酒店,最好有儿童乐园","商务出差需要的酒店,要交通方便", "价格便宜的住宿" ]for query in user_queries: print(f"\n用户查询: '{query}'") recommendations = recommender.recommend(query) print("推荐结果:")for i, hotel in enumerate(recommendations, 1): print(f"{i}. {hotel['name']} (相似度: {hotel['semantic_similarity']:.3f})") print(f" 描述: {hotel['description']}")demo_semantic_recommendation()我用Cursor简单的做了一个交互界面,给大家看一看动图8。
4.2 现代Embedding模型的发展
Word2Vec只是起点,现代Embedding技术已经发展到新的高度:
- BERT-based模型:考虑上下文双向信息
- Sentence-BERT:专门针对句子级嵌入优化
- OpenAI Embedding:基于大规模数据训练
- Jina Embedding:支持多语言和多模态(俄罗斯套娃)
# 使用现代句子嵌入模型的示例from sentence_transformers import SentenceTransformerdefmodern_embedding_demo():# 加载预训练模型 model = SentenceTransformer('all-MiniLM-L6-v2')# 句子列表 sentences = ["豪华海滨度假村拥有私人沙滩","这家酒店有专属海滩和泳池","商务型酒店位于市中心","股票市场今日上涨" ]# 生成嵌入向量 embeddings = model.encode(sentences) print(f"句子数量: {len(sentences)}") print(f"嵌入维度: {embeddings.shape[1]}") print(f"嵌入矩阵形状: {embeddings.shape}")# 计算相似度from sklearn.metrics.pairwise import cosine_similarity similarity_matrix = cosine_similarity(embeddings) print("\n语义相似度矩阵:")for i, sent in enumerate(sentences): print(f"{sent[:15]:<15}{similarity_matrix[i]}")modern_embedding_demo()五、 向量数据库: AI的记忆供电
5.1 为什么需要向量数据库
传统数据库擅长精确匹配,但在处理"找到与这个描述相似的酒店"这类语义搜索时力不从心。向量数据库专门为相似性搜索设计,能在毫秒级别从数百万向量中找到最相似的几个。哪怕你上传的知识内容可能总体质量不尽人意,但是也可以在毫秒级别找到其中最优的几个数据。
import faissimport numpy as npdefvector_database_demo():# 生成示例数据 n_hotels = 1000 embedding_dim = 384# 常用Embedding模型的维度# 模拟酒店向量(实际中来自Embedding模型) np.random.seed(42) hotel_vectors = np.random.random((n_hotels, embedding_dim)).astype('float32')# 创建FAISS索引 index = faiss.IndexFlatIP(embedding_dim) # 内积相似度 faiss.normalize_L2(hotel_vectors) # 归一化,内积=余弦相似度# 添加向量到索引 index.add(hotel_vectors) print(f"索引中的向量数量: {index.ntotal}")# 搜索示例 query_vector = np.random.random((1, embedding_dim)).astype('float32') faiss.normalize_L2(query_vector) k = 5# 返回最相似的5个 similarities, indices = index.search(query_vector, k) print(f"\n搜索返回的酒店索引: {indices[0]}") print(f"相似度得分: {similarities[0]}")vector_database_demo()5.2 主流向量数据库对比
| 数据库 | 核心特点 | 适用场景 |
| FAISS | Meta开发的高性能库,专注相似性搜索 | 研究、需要极致性能 |
| Milvus | 开源功能全面,支持分布式架构 | 企业级应用,需要扩展性 |
| Pinecone | 全托管服务,简单易用 | 快速原型开发,减少运维负担 |
| Elasticsearch | 传统搜索巨头,支持混合搜索 | 已在使用ES,需要增加向量搜索 |
FAISS是我现在正在用的,比较适合用于个人,轻量级的数据库构建。上面列举的这三个是企业级的,并且很多企业已经在使用了,大家也可以自己再多去了解这几个数据库,我就不多赘述了。
如何学习大模型 AI ?
由于新岗位的生产效率,要优于被取代岗位的生产效率,所以实际上整个社会的生产效率是提升的。
但是具体到个人,只能说是:
“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。
这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。
我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。
我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。
第一阶段(10天):初阶应用
该阶段让大家对大模型 AI有一个最前沿的认识,对大模型 AI 的理解超过 95% 的人,可以在相关讨论时发表高级、不跟风、又接地气的见解,别人只会和 AI 聊天,而你能调教 AI,并能用代码将大模型和业务衔接。
- 大模型 AI 能干什么?
- 大模型是怎样获得「智能」的?
- 用好 AI 的核心心法
- 大模型应用业务架构
- 大模型应用技术架构
- 代码示例:向 GPT-3.5 灌入新知识
- 提示工程的意义和核心思想
- Prompt 典型构成
- 指令调优方法论
- 思维链和思维树
- Prompt 攻击和防范
- …
第二阶段(30天):高阶应用
该阶段我们正式进入大模型 AI 进阶实战学习,学会构造私有知识库,扩展 AI 的能力。快速开发一个完整的基于 agent 对话机器人。掌握功能最强的大模型开发框架,抓住最新的技术进展,适合 Python 和 JavaScript 程序员。
- 为什么要做 RAG
- 搭建一个简单的 ChatPDF
- 检索的基础概念
- 什么是向量表示(Embeddings)
- 向量数据库与向量检索
- 基于向量检索的 RAG
- 搭建 RAG 系统的扩展知识
- 混合检索与 RAG-Fusion 简介
- 向量模型本地部署
- …
第三阶段(30天):模型训练
恭喜你,如果学到这里,你基本可以找到一份大模型 AI相关的工作,自己也能训练 GPT 了!通过微调,训练自己的垂直大模型,能独立训练开源多模态大模型,掌握更多技术方案。
到此为止,大概2个月的时间。你已经成为了一名“AI小子”。那么你还想往下探索吗?
- 为什么要做 RAG
- 什么是模型
- 什么是模型训练
- 求解器 & 损失函数简介
- 小实验2:手写一个简单的神经网络并训练它
- 什么是训练/预训练/微调/轻量化微调
- Transformer结构简介
- 轻量化微调
- 实验数据集的构建
- …
第四阶段(20天):商业闭环
对全球大模型从性能、吞吐量、成本等方面有一定的认知,可以在云端和本地等多种环境下部署大模型,找到适合自己的项目/创业方向,做一名被 AI 武装的产品经理。
- 硬件选型
- 带你了解全球大模型
- 使用国产大模型服务
- 搭建 OpenAI 代理
- 热身:基于阿里云 PAI 部署 Stable Diffusion
- 在本地计算机运行大模型
- 大模型的私有化部署
- 基于 vLLM 部署大模型
- 案例:如何优雅地在阿里云私有部署开源大模型
- 部署一套开源 LLM 项目
- 内容安全
- 互联网信息服务算法备案
- …
学习是一个过程,只要学习就会有挑战。天道酬勤,你越努力,就会成为越优秀的自己。
如果你能在15天内完成所有的任务,那你堪称天才。然而,如果你能完成 60-70% 的内容,你就已经开始具备成为一名大模型 AI 的正确特征了。