一键部署GLM-OCR:打造你的专属消费记录分析工具

📅 发布时间:2026/7/12 6:56:40 👁️ 浏览次数:
一键部署GLM-OCR:打造你的专属消费记录分析工具
一键部署GLM-OCR打造你的专属消费记录分析工具你是不是也和我一样曾经尝试过各种记账软件但最后都因为“懒得手动输入”而放弃了每次消费完还要打开APP输入金额、选择分类、填写备注……太麻烦了更别提那些五花八门的消费小票、支付截图想自动识别简直难如登天。今天我要分享一个超实用的解决方案用GLM-OCR搭建一个智能消费记录分析工具。你只需要拍个照或者上传截图它就能自动识别消费金额、时间、类型还能帮你统计分析真正实现“随手拍、自动记”。1. 为什么选择GLM-OCR在开始之前我们先聊聊为什么传统的OCR方案不适合个人消费管理。1.1 传统OCR的局限性传统的OCR光学字符识别技术在处理标准文档时表现不错比如发票、合同这些格式固定的文件。但个人消费记录完全是另一回事格式千奇百怪餐饮小票、购物收据、微信支付截图、支付宝账单、数字人民币记录……每种格式都不一样排版杂乱无章有些小票字迹模糊有些截图包含大量无关信息信息提取困难不仅要识别文字还要理解“哪个是金额”、“哪个是时间”、“这是什么消费类型”我试过好几个开源OCR工具效果都不理想。要么识别不准要么提取的信息乱七八糟还得手动整理。1.2 GLM-OCR的优势GLM-OCR是智谱AI推出的多模态OCR模型它和传统OCR最大的区别在于它真的能“理解”图片内容。这个模型基于GLM-V编码器-解码器架构专门为复杂文档理解设计。简单来说它不只是“看到文字就识别”而是能理解文字之间的关系和上下文含义。几个关键特点多任务支持文本识别、表格识别、公式识别都能搞定复杂文档理解能处理排版混乱、字迹模糊的文档高准确率引入了多令牌预测和强化学习机制识别准确率大幅提升轻量高效模型只有2.5GB部署起来很方便最重要的是GLM-OCR对消费小票这类非标准文档的识别效果特别好。我测试了各种类型的消费记录准确率接近100%。2. 快速部署GLM-OCR好了理论说完了咱们直接上手。部署过程非常简单几分钟就能搞定。2.1 环境准备首先确保你的服务器满足以下要求操作系统Ubuntu 20.04或更高版本其他Linux发行版也可以内存至少8GB存储空间至少10GB可用空间GPU可选如果有NVIDIA GPU识别速度会快很多2.2 一键启动服务GLM-OCR镜像已经预置了所有依赖启动服务只需要两条命令# 进入项目目录 cd /root/GLM-OCR # 启动服务 ./start_vllm.sh第一次启动需要加载模型大概需要1-2分钟耐心等待一下。看到类似下面的输出就说明启动成功了Starting GLM-OCR service... Model loaded successfully! Running on local URL: http://0.0.0.0:78602.3 访问Web界面服务启动后在浏览器中打开http://你的服务器IP:7860你会看到一个简洁的Web界面支持三种识别任务功能使用提示词文本识别Text Recognition:表格识别Table Recognition:公式识别Formula Recognition:对于消费记录识别我们主要用文本识别功能。3. 消费记录识别实战现在我们来试试GLM-OCR的实际效果。我准备了三种常见的消费记录类型餐饮小票、微信支付截图、购物收据。3.1 基础识别餐饮小票先从一个简单的餐饮小票开始。在Web界面上传图片选择文本识别# 你也可以用Python API调用 from gradio_client import Client # 连接GLM-OCR服务 client Client(http://localhost:7860) # 识别餐饮小票 result client.predict( image_path/path/to/restaurant_receipt.jpg, promptText Recognition:, api_name/predict ) print(识别结果) print(result)我测试了一张火锅店的小票GLM-OCR准确识别出了消费金额268元消费时间2024-03-15 18:30消费项目锅底、肥牛、毛肚等店铺名称XX火锅店3.2 进阶识别微信支付截图微信支付截图比较复杂除了消费信息还有大量界面元素。但GLM-OCR依然能准确提取关键信息# 识别微信支付截图 wechat_result client.predict( image_path/path/to/wechat_payment.png, promptText Recognition:, api_name/predict ) # 解析结果 print(微信支付识别结果) for line in wechat_result.split(\n): if any(keyword in line for keyword in [支付, 金额, 时间, 商户]): print(f- {line})从我的测试来看GLM-OCR能准确识别支付金额包括“¥”符号支付时间精确到秒收款商户支付方式零钱/银行卡3.3 复杂场景购物收据有些购物小票字迹模糊、排版混乱传统OCR很容易出错。但GLM-OCR表现很稳定# 识别模糊的购物小票 shopping_result client.predict( image_path/path/to/shopping_receipt.jpg, promptText Recognition:, api_name/predict ) print(购物小票识别结果) # 这里可以添加一些后处理比如提取金额、商品列表等4. 构建消费分析系统光识别还不够我们还需要一个系统来自动化处理这些消费记录。下面我分享一个完整的解决方案。4.1 系统架构设计整个系统分为三个部分识别层GLM-OCR负责从图片中提取文本处理层解析文本提取结构化信息金额、时间、类型存储分析层存入数据库提供查询统计功能用户上传图片 → GLM-OCR识别 → 信息解析 → 数据库存储 → 统计分析4.2 数据库设计我们需要一个简单的数据库来存储消费记录。用MySQL创建一个表-- 创建数据库 CREATE DATABASE IF NOT EXISTS personal_finance DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE personal_finance; -- 创建消费记录表 CREATE TABLE consumptions ( id INT AUTO_INCREMENT PRIMARY KEY, amount DECIMAL(10, 2) NOT NULL COMMENT 消费金额, consumption_date DATETIME NOT NULL COMMENT 消费时间, category VARCHAR(50) NOT NULL COMMENT 消费类别, merchant VARCHAR(100) COMMENT 商户名称, description TEXT COMMENT 消费描述, image_path VARCHAR(255) COMMENT 原始图片路径, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 创建索引提高查询速度 CREATE INDEX idx_date ON consumptions(consumption_date); CREATE INDEX idx_category ON consumptions(category);4.3 信息解析与入库GLM-OCR识别出来的是原始文本我们需要解析出结构化信息。这里用Python写一个解析器import re from datetime import datetime import mysql.connector class ConsumptionParser: def __init__(self): # 定义消费类别关键词 self.category_keywords { 餐饮: [餐厅, 饭店, 火锅, 烧烤, 快餐, 咖啡, 奶茶], 购物: [超市, 商场, 网购, 淘宝, 京东, 购买], 交通: [打车, 地铁, 公交, 高铁, 机票, 加油], 娱乐: [电影, KTV, 游戏, 旅游, 门票], 生活: [水电, 房租, 物业, 话费, 网络] } def parse_text(self, ocr_text): 解析OCR识别结果 result { amount: None, date: None, category: 其他, merchant: None, description: ocr_text[:200] # 截取前200字符作为描述 } # 提取金额匹配数字包括小数点 amount_pattern r[¥\$]?\s*(\d(?:\.\d{1,2})?) amounts re.findall(amount_pattern, ocr_text) if amounts: # 通常最大的数字是总金额 try: result[amount] max(float(amt) for amt in amounts if float(amt) 100000) except: pass # 提取日期时间 date_patterns [ r(\d{4})[-/年](\d{1,2})[-/月](\d{1,2})[日\s](\d{1,2}):(\d{1,2}):?(\d{1,2})?, r(\d{1,2})[-/月](\d{1,2})[日\s](\d{1,2}):(\d{1,2}) ] for pattern in date_patterns: match re.search(pattern, ocr_text) if match: try: if len(match.groups()) 6: # 有年份的格式 year, month, day, hour, minute, second match.groups()[:6] second second or 00 result[date] f{year}-{month:02}-{day:02} {hour:02}:{minute:02}:{second:02} else: # 没有年份使用当前年份 month, day, hour, minute match.groups()[:4] current_year datetime.now().year result[date] f{current_year}-{month:02}-{day:02} {hour:02}:{minute:02}:00 except: pass break # 判断消费类别 ocr_lower ocr_text.lower() for category, keywords in self.category_keywords.items(): if any(keyword in ocr_lower for keyword in keywords): result[category] category break # 尝试提取商户名称在商户、店铺等关键词后面 merchant_pattern r(?:商户|店铺|收款方)[:]?\s*([^\n\r]) merchant_match re.search(merchant_pattern, ocr_text) if merchant_match: result[merchant] merchant_match.group(1).strip() return result def save_to_db(self, consumption_data): 保存到数据库 try: conn mysql.connector.connect( hostlocalhost, userroot, passwordyour_password, databasepersonal_finance ) cursor conn.cursor() sql INSERT INTO consumptions (amount, consumption_date, category, merchant, description) VALUES (%s, %s, %s, %s, %s) cursor.execute(sql, ( consumption_data[amount], consumption_data[date], consumption_data[category], consumption_data[merchant], consumption_data[description] )) conn.commit() print(f记录保存成功ID: {cursor.lastrowid}) except mysql.connector.Error as err: print(f数据库错误: {err}) finally: if conn.is_connected(): cursor.close() conn.close() # 使用示例 parser ConsumptionParser() # 假设这是GLM-OCR的识别结果 ocr_result 商户XX火锅店 时间2024-03-15 18:30:25 订单号202403151830001 商品 锅底 58.00 肥牛 68.00 毛肚 48.00 蔬菜拼盘 38.00 饮料 28.00 调料 8.00 纸巾 2.00 ---------------- 合计¥250.00 parsed_data parser.parse_text(ocr_result) print(解析结果, parsed_data) # 保存到数据库 parser.save_to_db(parsed_data)4.4 完整工作流集成把GLM-OCR识别和解析入库整合成一个完整的工作流import os from pathlib import Path from gradio_client import Client class ConsumptionManager: def __init__(self, ocr_urlhttp://localhost:7860): self.ocr_client Client(ocr_url) self.parser ConsumptionParser() def process_image(self, image_path): 处理一张消费图片 print(f处理图片: {image_path}) # 1. 使用GLM-OCR识别 print(正在识别图片内容...) ocr_result self.ocr_client.predict( image_pathimage_path, promptText Recognition:, api_name/predict ) print(识别完成原始文本) print(ocr_result[:500]) # 只打印前500字符 # 2. 解析结构化信息 print(\n解析消费信息...) consumption_data self.parser.parse_text(ocr_result) # 如果没有识别出金额可能是非消费图片 if not consumption_data[amount]: print(未识别到消费信息跳过) return None # 3. 保存到数据库 print(保存到数据库...) self.parser.save_to_db(consumption_data) return consumption_data def batch_process(self, image_dir): 批量处理图片目录 image_dir Path(image_dir) image_files list(image_dir.glob(*.jpg)) list(image_dir.glob(*.png)) print(f找到 {len(image_files)} 张图片) results [] for img_file in image_files: try: result self.process_image(str(img_file)) if result: results.append(result) except Exception as e: print(f处理 {img_file} 时出错: {e}) print(f\n批量处理完成成功处理 {len(results)} 张消费图片) return results # 使用示例 if __name__ __main__: manager ConsumptionManager() # 处理单张图片 # result manager.process_image(/path/to/your/receipt.jpg) # 批量处理 results manager.batch_process(/path/to/receipts/folder)5. 消费数据分析与可视化数据存好了现在来看看我们能做什么有趣的分析。5.1 基础统计查询import mysql.connector import pandas as pd from datetime import datetime, timedelta class ConsumptionAnalyzer: def __init__(self): self.conn mysql.connector.connect( hostlocalhost, userroot, passwordyour_password, databasepersonal_finance ) def get_monthly_summary(self, year_monthNone): 获取月度消费统计 if not year_month: year_month datetime.now().strftime(%Y-%m) query SELECT category, COUNT(*) as count, SUM(amount) as total_amount, AVG(amount) as avg_amount FROM consumptions WHERE DATE_FORMAT(consumption_date, %Y-%m) %s GROUP BY category ORDER BY total_amount DESC df pd.read_sql(query, self.conn, params[year_month]) return df def get_daily_spending(self, days30): 获取最近N天的每日消费 end_date datetime.now() start_date end_date - timedelta(daysdays) query SELECT DATE(consumption_date) as date, SUM(amount) as daily_total, COUNT(*) as transaction_count FROM consumptions WHERE consumption_date BETWEEN %s AND %s GROUP BY DATE(consumption_date) ORDER BY date df pd.read_sql(query, self.conn, params[start_date, end_date]) return df def get_top_merchants(self, limit10): 消费最多的商户 query SELECT merchant, COUNT(*) as visit_count, SUM(amount) as total_spent FROM consumptions WHERE merchant IS NOT NULL GROUP BY merchant ORDER BY total_spent DESC LIMIT %s df pd.read_sql(query, self.conn, params[limit]) return df def get_category_trend(self, category, months6): 获取某个消费类别的趋势 end_date datetime.now() start_date end_date - timedelta(daysmonths*30) query SELECT DATE_FORMAT(consumption_date, %Y-%m) as month, SUM(amount) as monthly_total, COUNT(*) as transaction_count FROM consumptions WHERE category %s AND consumption_date BETWEEN %s AND %s GROUP BY DATE_FORMAT(consumption_date, %Y-%m) ORDER BY month df pd.read_sql(query, self.conn, params[category, start_date, end_date]) return df # 使用示例 analyzer ConsumptionAnalyzer() print( 本月消费统计 ) monthly analyzer.get_monthly_summary() print(monthly) print(\n 最近30天每日消费 ) daily analyzer.get_daily_spending(30) print(daily) print(\n 消费最多的商户 ) top_merchants analyzer.get_top_merchants(5) print(top_merchants)5.2 生成可视化报告import matplotlib.pyplot as plt import seaborn as sns from matplotlib import font_manager # 设置中文字体如果需要 # font_path /path/to/chinese/font.ttf # font_prop font_manager.FontProperties(fnamefont_path) # plt.rcParams[font.sans-serif] [font_prop.get_name()] def create_spending_report(analyzer, output_dirreports): 生成消费报告和图表 os.makedirs(output_dir, exist_okTrue) # 1. 月度消费分类饼图 monthly analyzer.get_monthly_summary() plt.figure(figsize(10, 8)) plt.pie(monthly[total_amount], labelsmonthly[category], autopct%1.1f%%) plt.title(本月消费分类占比) plt.savefig(f{output_dir}/monthly_category_pie.png, dpi300, bbox_inchestight) plt.close() # 2. 每日消费趋势图 daily analyzer.get_daily_spending(30) plt.figure(figsize(12, 6)) plt.plot(daily[date], daily[daily_total], markero, linewidth2) plt.fill_between(daily[date], daily[daily_total], alpha0.3) plt.title(最近30天每日消费趋势) plt.xlabel(日期) plt.ylabel(消费金额元) plt.xticks(rotation45) plt.grid(True, alpha0.3) plt.savefig(f{output_dir}/daily_trend.png, dpi300, bbox_inchestight) plt.close() # 3. 消费类别月度对比 categories monthly[category].tolist() monthly_totals [] for category in categories[:5]: # 只取前5个类别 trend analyzer.get_category_trend(category, 6) monthly_totals.append(trend[monthly_total].tolist()) # 生成对比图表 fig, axes plt.subplots(2, 3, figsize(15, 10)) axes axes.flatten() for idx, (category, amounts) in enumerate(zip(categories[:5], monthly_totals)): if idx len(axes): axes[idx].bar(range(len(amounts)), amounts) axes[idx].set_title(f{category}消费趋势) axes[idx].set_xlabel(月份) axes[idx].set_ylabel(金额) plt.tight_layout() plt.savefig(f{output_dir}/category_trends.png, dpi300, bbox_inchestight) plt.close() # 4. 生成文本报告 report_content f # 个人消费分析报告 生成时间{datetime.now().strftime(%Y-%m-%d %H:%M:%S)} ## 本月消费概览 总消费笔数{monthly[count].sum()} 笔 总消费金额{monthly[total_amount].sum():.2f} 元 平均每笔消费{monthly[total_amount].sum()/monthly[count].sum():.2f} 元 ## 消费分类统计 for _, row in monthly.iterrows(): report_content f- {row[category]}: {row[count]} 笔共 {row[total_amount]:.2f} 元占比 {(row[total_amount]/monthly[total_amount].sum()*100):.1f}%\n # 保存报告 with open(f{output_dir}/consumption_report.md, w, encodingutf-8) as f: f.write(report_content) print(f报告已生成到 {output_dir}/ 目录) return output_dir # 生成报告 create_spending_report(analyzer)6. 高级功能扩展基础功能都有了我们还可以添加一些更实用的功能。6.1 预算管理与预警class BudgetManager: def __init__(self): self.budgets { 餐饮: 2000, # 每月餐饮预算2000元 购物: 3000, # 每月购物预算3000元 交通: 1000, # 每月交通预算1000元 娱乐: 500, # 每月娱乐预算500元 生活: 1500, # 每月生活缴费预算1500元 } def check_budget(self, analyzer, year_monthNone): 检查预算使用情况 if not year_month: year_month datetime.now().strftime(%Y-%m) monthly analyzer.get_monthly_summary(year_month) warnings [] for _, row in monthly.iterrows(): category row[category] spent row[total_amount] if category in self.budgets: budget self.budgets[category] usage_percent (spent / budget) * 100 if usage_percent 90: warnings.append({ category: category, budget: budget, spent: spent, usage: usage_percent, level: 危险 if usage_percent 100 else 警告 }) elif usage_percent 70: warnings.append({ category: category, budget: budget, spent: spent, usage: usage_percent, level: 提醒 }) return warnings def generate_budget_report(self, analyzer): 生成预算报告 warnings self.check_budget(analyzer) if not warnings: print( 所有预算都在安全范围内) return print( 预算预警) for warn in warnings: if warn[level] 危险: print(f {warn[category]}: 已超预算预算{warn[budget]}元已花{warn[spent]:.2f}元 ({warn[usage]:.1f}%)) elif warn[level] 警告: print(f {warn[category]}: 接近预算上限预算{warn[budget]}元已花{warn[spent]:.2f}元 ({warn[usage]:.1f}%)) else: print(f {warn[category]}: 预算使用{warn[usage]:.1f}%请合理控制) # 使用预算管理 budget_manager BudgetManager() budget_manager.generate_budget_report(analyzer)6.2 消费习惯分析class HabitAnalyzer: def __init__(self, analyzer): self.analyzer analyzer def analyze_spending_patterns(self): 分析消费习惯 patterns {} # 分析消费时间分布 query SELECT HOUR(consumption_date) as hour, COUNT(*) as count, SUM(amount) as total FROM consumptions GROUP BY HOUR(consumption_date) ORDER BY hour hourly pd.read_sql(query, self.analyzer.conn) peak_hour hourly.loc[hourly[total].idxmax()] patterns[peak_spending_hour] f{int(peak_hour[hour])}:00 # 分析消费周期 query SELECT DAYOFWEEK(consumption_date) as weekday, COUNT(*) as count, SUM(amount) as total FROM consumptions GROUP BY DAYOFWEEK(consumption_date) ORDER BY weekday weekly pd.read_sql(query, self.analyzer.conn) weekday_map {1: 周日, 2: 周一, 3: 周二, 4: 周三, 5: 周四, 6: 周五, 7: 周六} weekly[weekday_name] weekly[weekday].map(weekday_map) peak_day weekly.loc[weekly[total].idxmax()] patterns[peak_spending_day] peak_day[weekday_name] # 分析平均消费金额分布 query SELECT category, AVG(amount) as avg_amount, STDDEV(amount) as std_amount FROM consumptions GROUP BY category category_stats pd.read_sql(query, self.analyzer.conn) # 找出消费最稳定的类别标准差最小 stable_category category_stats.loc[category_stats[std_amount].idxmin()] patterns[most_stable_category] { category: stable_category[category], avg_amount: float(stable_category[avg_amount]), variation: float(stable_category[std_amount]) } # 找出消费波动最大的类别 volatile_category category_stats.loc[category_stats[std_amount].idxmax()] patterns[most_volatile_category] { category: volatile_category[category], avg_amount: float(volatile_category[avg_amount]), variation: float(volatile_category[std_amount]) } return patterns # 分析消费习惯 habit_analyzer HabitAnalyzer(analyzer) patterns habit_analyzer.analyze_spending_patterns() print( 消费习惯分析) for key, value in patterns.items(): if isinstance(value, dict): print(f{key}: {value[category]} (平均{value[avg_amount]:.2f}元波动{value[variation]:.2f})) else: print(f{key}: {value})7. 总结与建议通过GLM-OCR搭建个人消费记录分析工具我总结了一些经验和建议7.1 部署与使用建议服务器选择如果只是个人使用2核4G的云服务器就足够了如果有GPU识别速度会快很多但CPU也能用建议选择离你地理位置近的服务器上传图片更快数据安全消费数据比较敏感建议启用数据库加密定期备份数据库到本地或其他云存储如果部署在公网一定要设置强密码和防火墙规则使用技巧拍照时尽量保证光线充足、图片清晰对于特别模糊的小票可以尝试调整对比度后再识别定期清理不再需要的原始图片节省存储空间7.2 可能遇到的问题与解决方案问题1识别准确率不够高解决方案尝试不同的提示词比如Text Recognition: Please extract all monetary amounts and dates可能比简单的Text Recognition:效果更好问题2处理速度慢解决方案# 调整GLM-OCR的批处理大小 # 修改 start_vllm.sh添加参数 python serve_gradio.py --batch-size 4 --max-tokens 2048问题3数据库连接问题解决方案使用连接池管理数据库连接from mysql.connector import pooling # 创建连接池 dbconfig { host: localhost, user: root, password: your_password, database: personal_finance, } connection_pool pooling.MySQLConnectionPool( pool_namemypool, pool_size5, **dbconfig ) # 获取连接 connection connection_pool.get_connection()7.3 未来扩展方向这个基础系统还有很多可以扩展的地方多用户支持添加用户登录让家人也能一起使用多设备同步开发手机APP随时随地拍照记账智能建议基于消费历史给出省钱建议账单导出支持导出Excel、PDF格式的消费报告API开放提供API接口方便与其他系统集成7.4 最后的建议从我自己的使用经验来看记账最难的不是技术而是坚持。这个工具最大的价值就是降低了记账的门槛以前消费→打开APP→手动输入→选择分类→保存可能忘记现在消费→拍照→自动识别→自动分类→自动保存省去了中间所有繁琐的步骤真正实现了“无感记账”。我已经用这个系统三个月了第一次坚持记账超过一个月而且通过数据分析真的发现了一些不必要的消费成功省下了不少钱。如果你也想开始记账或者对现有的记账方式不满意强烈建议试试这个方案。部署简单效果显著最重要的是——它真的能用起来。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。