设计一个基于大数据的机器学习全自动记账系统(AI篇)
设计一个基于大数据的机器学习全自动记账系统(AI篇)
随着基于大语言模型的 AI 技术发展越来越迅猛,使用自然语言处理大数据技术来设计一个全自动记账系统已经不再是梦想。本文将介绍如何基于个人过往消费的大数据,设计一个专属的全自动账单分类记账系统。
如何将一笔交易自动分类
如果我们需要使用机器学习的方式来进行账单的自动分类,那么我们首先需要解决的问题就是如何将账单数据转化为机器学习算法可以识别的数据。我们需要根据自己过往的账单消费数据生成一个特征模型。也就是告诉机器,符合什么特征的消费数据属于哪一个分类的可能性最高。
通常我们自己对账时,一般是通过观察一笔交易的商品名称,或者交易对象,并回忆该笔交易发生时的相关 context 来判断该数据更符合哪一个分类。对于机器来说,它能获取到该笔交易的商品名称与交易对象,但无法获取到每一笔交易发生时的 context。可是它能通过对比过去你做相似交易时的分类作为参考依据生成判断。因此,通过商品名称与交易对象生成特征向量就很自然的成为了一个解决方案。
我们知道,处理商品名称与交易对象的特征相比普通自然语言处理的好处在于,它可以被视为预处理过后的数据。因此我们可以跳过文本清洗(通常是指去除标点符号,连词,大小写转换等)过程,直接进行特征提取。
而对于中文语料,特征提取最常用的方案就是通过对特征数据进行分词处理,攫取特征生成特征向量集,从而训练一个自定义机器学习模型。这可能涉及到的算法有朴素贝叶斯,支持向量机,决策树,随机森林,逻辑回归,深度学习等。在训练过程中,模型会学习从评论的特征到其相应类别的映射。
在训练好模型后,我们就可以使用这个模型来对新的账单数据进行分类了。当然,这个模型的准确性与泛化能力取决于你的训练数据的质量与数量。因此,我们需要尽可能多的标记自己的账单数据,以提高模型的准确性。
传统机器学习方案的局限性
以上是使用机器学习的方式来进行账单分类的基本思路,也是之前尝试使用的做法。但是在实际操作中,发现这种方式存在一些问题:
- 由于交易数据涵盖范围广且杂乱,需要大量数据来训练模型以提高准确性。但个人标记的数据量有限,导致模型准确性不高。
- 基于中文分词库生成的特征向量,对于中英文混杂或语句不通顺的商品名称,分词结果可能不够准确,导致特征向量生成杂乱,可靠性不高。
- 许多传统机器学习算法基于线性假设,难以捕捉高维向量数据中的非线性模式。
- 特征工程和模型调优需要专业知识,对于业余爱好者来说是个挑战。
最终,虽然尝试完成了一个算法并用于账单自动分类,但准确度一直是个问题。如果将置信度调得过高,效果甚至不如直接使用之前的过滤逻辑判断。
Chroma DB 的引入与使用
事情的转机来自于专业人士的建议。在工作中,通过与机器学习专家的讨论,引入了 Chroma DB,一个向量数据库作为黑盒的特征处理工具。
Chroma DB 是一种支持向量搜索的数据库,也被称为向量搜索引擎。它是一种特殊类型的数据库,专门设计用于存储和检索大规模的高维向量数据。它的主要功能是提供高效的向量相似性搜索。
传统的关系型数据库或文档型数据库主要是基于精确匹配或关键词搜索,对于高维向量这种类型的数据,这种搜索方式效率低下。而 Chroma DB 则可以通过计算向量之间的余弦相似度或欧氏距离等方式,来找到与查询向量最相似的向量,从而实现高效的相似性搜索。
初始化数据库并注入训练集
最难的技术选型确立后,接下来的工作就变得相对简单了。我们需要初始化 Chroma DB,并将已经完成标注的账单数据导入 Chroma DB 中,然后通过调用 Chroma DB 的 API 来进行账单分类。
这里选用 Python 的 Chroma DB SDK。Chroma DB 也原生支持 JavaScript SDK,可以根据需要选择合适的 SDK。安装相关依赖等过程可以参考 Chroma DB 的官方文档。
首先需要选择一个合适的预训练模型,这里选择了 moka-ai/m3e-base,这是一个基于 m3e 的中文语言模型,可以很好地处理中文文本。通过 SentenceTransformer 这个转换器模型将输入的数据通过这个预训练模型转换为嵌入式向量。
之后初始化 Chroma DB,创建一个新的 collection,这个 collection 将用于存储和检索嵌入向量。在 metadata 中指定了使用余弦空间进行 hnsw 搜索,这里的 hnsw 是一种用于大规模相似性搜索的有效算法。
from chromadb import Documents, EmbeddingFunction, Embeddings
import chromadb
from sentence_transformers import SentenceTransformer
embedding_model = SentenceTransformer('moka-ai/m3e-base')
db_client = chromadb.PersistentClient(path="lib/chromadb")
class MyEmbeddingFunction(EmbeddingFunction):
def __call__(self, input: Documents) -> Embeddings:
batch_embeddings = embedding_model.encode(input)
return batch_embeddings.tolist()
embed_fn = MyEmbeddingFunction()
# create collection
collection = db_client.get_or_create_collection(
name=f"bookkeeping-vector-db",
embedding_function=embed_fn,
metadata={"hnsw:space": "cosine"}
)
之后导入已经标注好的训练集,并将训练集一条一条地注入到 Chroma DB 的 collection 中。可以看到,将每一条交易数据的交易对方与商品名称作为文本输入,将交易的分类作为 metadata 输入。这样在后续的交易数据查询预测中,就可以通过输入交易对方与商品名称来预测该笔交易的分类。
import pandas as pd
import uuid
training_df = pd.read_csv(dataset_path)
for idx, data in training_df.iterrows():
metadata = {
"type":data['类型'],
}
sentence = f"{data['交易对方']}:{data['商品名称']}"
collection.add(
documents = [sentence],
metadatas = [metadata],
ids = [str(uuid.uuid4())]
)
尝试预测分类结果
模型训练好后,可以尝试将单条交易记录输入检测模型的训练效果。通过调用 Chroma DB 的 query 方法来进行相似性搜索,找到与输入向量匹配度最高的 metadata,从而得到该笔交易的分类。
from sentence_transformers import SentenceTransformer
embedding_model = SentenceTransformer('moka-ai/m3e-base')
query = "这里填写交易记录的交易对方与商品名称"
embedding_to_query = embedding_model.encode([query])
results = collection.query(embedding_to_query, n_results=1)
在这个执行语句中,仅选取了 topN = 1 的数据,直接判断最相似的数据作为该笔交易的分类。它的执行结果如下:其中 distances 表示的是查询向量与结果向量的相似度,这个值越小,可以认为模型认为属于该种分类的可能性越高。metadatas 中的 type 即为查询向量对应的分类结果。因此可以得出大模型认为该笔交易更可能属于娱乐类型消费。
{
"ids": [["26607b02-a85a-4bb4-b068-d917c80d4583"]],
"distances": [[0.15959841012954712]],
"metadatas": [[{"type": "娱乐"}]],
"embeddings": None,
"uris": None,
"data": None
}
设计相似性加权算法,提升分类准确性
仅根据与某一条消费数据的相似性得出结论过于简单。可以扩大 topN 的值,或者设计一个相似度算法来辅助判断。例如,可能遇到这样一种情况:某笔交易与某一笔娱乐类型消费的相似度最大,但大数据统计显示相似度排名二到五的消费数据都显示该笔交易更可能属于餐饮类型的消费。
这时可以设计一个相似性加权算法,来综合考虑 topN = 5 的数据,从而提升分类的准确性。
import numpy as np
from scipy.special import softmax
def getPredictedType(distances: list, types: list):
distance = np.array(distances)
distance[distance == 0] = 1e-10 # replace zeros with a small number
distance = softmax(1 / distance)
result = {}
for score, item in zip(distance, types):
if item in result:
result[item] += score
else:
result[item] = score
if max(result.values()) > temperature:
return max(result, key=result.get)
else:
return None
这个算法的思路是,将 topN 的相似度数据进行 softmax 处理,得到一个概率分布。然后将这个概率分布与对应的分类进行加权,得到一个加权后的分类结果。最后可以通过设置一个温度值,来判断加权后的分类结果是否达到了置信度。
批量导入并处理预测结果
将特征数据一条一条手动喂给模型显然不现实,需要一个自动化的流程,将账单自动进行分类预测。有了上面的铺垫,这一步的实现非常简单。只需要将表格读取,循环每一条特征数据,将生成预测的结果写回到表格中即可。
prediction_df = pd.read_csv(merged_bill_path)
for index, data in prediction_df.iterrows():
query = f"{data['交易对方']}:{data['商品名称']}"
embedding_to_query = embedding_model.encode([query])
results = collection.query(
embedding_to_query,
n_results=top_n
)
distances = results["distances"][0]
types = results["metadatas"][0]
all_types = [item["type"] for item in types]
predicted_type = getPredictedType(distances, all_types)
if predicted_type is not None:
prediction_df.loc[index, '类型'] = predicted_type
prediction_df.to_csv(predict_bill_path, encoding='utf-8', index=False)
这样一来,就可以在设置好的 predict_bill_path 路径中,收获到一份自动分类的账单数据了。
写在末尾
记账的过程本就是一个繁琐且复杂的过程,而利用技术尽可能自动化地完成他们本身就是一个很有成就感且很有意义的事情。在拥有机器学习的能力加持后,一切的账单分析及分类变得越来越无感和高效。
而在拥有了越来越充足的账单数据后,可以进行更进一步的数据分析,生成各种维度的消费及财务管理图表,从而更直观地了解自己的消费习惯与消费趋势,为自己更好地规划财务计划,早日实现财务自由。