GBDT:从残差到梯度提升树的完整理解
GBDT(Gradient Boosting Decision Tree,梯度提升决策树)是机器学习中非常常用的一类模型,尤其适合表格数据任务。 它的名字看起来很长,但拆开以后并不神秘: Gradient:沿着损失函数下降的方向修正模型。 Boosting:一轮一轮训练模型,后面的模型修正前面的错误。 Decision Tree:每一轮加入的基模型通常是一棵回归树。 一句话概括: GBDT 不是一次训练一棵很大的树,而是一棵树接一棵树地补前面模型的错误。 1. 为什么需要 GBDT单棵决策树有两个明显问题: 树太浅,模型表达能力不够,容易欠拟合。 树太深,容易把训练数据记得太死,导致过拟合。 GBDT 的思路不是训练一棵特别复杂的树,而是训练很多棵相对简单的树。 每一棵树只做一件事: 修正当前模型还没预测好的部分 这样模型可以逐步变强,同时通过学习率、树深度、树数量等参数控制复杂度。 2. 从一个房价例子理解 GBDT假设我们要预测 3 套房子的价格: 房子 面积 房龄 真实价格 A 70 平 15 年 100 万 B 100 平 8 年 16...
AICompanion 开发日志 04:真实模型接入诊断与情感交互方向校准
上一篇日志整理了 AICompanion 当前已经实现的功能:PySide6 聊天窗口、SQLite 本地存储、长期记忆、情绪状态、主动消息雏形,以及 OpenAI-compatible 大模型接口。 写完之后,我继续往前推进了一小步。我开始把项目从本地 mock 回复,推进到更稳定地接入真实大模型。 同时我也重新校准了一下项目定位。AICompanion 不是要做成一个传统桌面助手,不是以任务管理、效率工具或系统控制为核心。它最终想实现的是一个基于 Live2D 动漫角色的情感交互应用。也就是说,后续所有模块都应该服务于“角色感”“陪伴感”“情绪反馈”和“长期关系感”。 1. 这次新增内容概览这次主要新增和完善的是大模型接入体验,集中在 llm 模块和 README 文档中。 当前相关文件包括: src/aicompanion/llm/ base.py mock_client.py openai_compatible_client.py factory.py diagnostics.py 其中这次比较重要的是: src/aicompanion/llm/...
集成学习:让多个模型一起做决定
前面学习决策树时,我们提到过一个问题:单棵决策树虽然直观、可解释,但很容易过拟合,也很容易受到数据扰动影响。 集成学习(Ensemble Learning)就是为了解决这类问题而出现的思想。它不再把希望全部压在一个模型身上,而是训练多个模型,再把它们的结果组合起来。 可以把它理解成:一个人判断可能会偏,很多人从不同角度判断,再投票或取平均,结果通常更稳定。 1. 什么是集成学习集成学习的核心思想很简单: 训练多个基模型,再把这些模型的预测结果组合成最终结果。 这里的基模型也叫弱学习器(Weak Learner)或基学习器(Base Learner)。它不一定真的很弱,只是相对于最终组合模型来说,每个单独模型只负责贡献一部分判断。 例如一个分类任务中,我们训练了 5 个模型,它们分别预测: 模型 1:通过 模型 2:通过 模型 3:不通过 模型 4:通过 模型 5:通过 如果使用多数投票,最终结果就是“通过”。 如果是回归任务,例如预测房价,多个模型分别预测: 90 万、94 万、92 万、96 万、93 万 最终可以取平均值: $$\hat{y}=\fra...
AICompanion 开发日志 03:当前版本功能总览
前两篇分别记录了项目搭建和 PySide6 聊天窗口的实现。这一篇不继续单独展开某一个模块,而是做一次阶段性整理:把目前代码里已经实现的功能完整梳理一遍。 AICompanion 现在还不是最终形态,还没有 TTS,也还没有 Live2D。但它已经不只是一个空窗口了。目前这个版本已经把桌面聊天、模型接口、本地存储、长期记忆、情绪状态和主动消息的基础流程都接了起来。 1. 当前项目状态当前项目路径是: D:\Study\AICompanion 项目采用 src layout,核心代码放在: src/aicompanion/ 目前主要模块如下: src/aicompanion/ app.py # 应用装配入口 config.py # 配置读取 controllers/ chat_controller.py # 聊天流程控制 domain/ character.py # 角色设定 emotion.py # 情绪状态 llm...
AICompanion 开发日志 02:用 PySide6 搭建聊天窗口
上一篇主要记录了 AICompanion 的项目搭建、技术选型和目录结构。这一篇开始进入真正的功能实现。 当前阶段的目标很明确:先用 PySide6 做出一个可以运行的聊天窗口。它不需要一开始就很华丽,也不需要马上接入 Live2D,但必须把后续扩展需要的基础结构搭好。 也就是说,这一篇的重点不是“画一个窗口”,而是做出一个可以继续生长的桌面聊天程序骨架。 1. 当前阶段目标AICompanion 的整体计划分成几个阶段: 阶段 目标 第一阶段 Python + PySide6 做聊天窗口 第二阶段 接入大模型,实现多轮聊天 第三阶段 SQLite 保存聊天记录和记忆 第四阶段 主动消息、角色设定、表情切换 第五阶段 加入 TTS 语音回复 第六阶段 尝试 Live2D 这篇文章对应第一阶段的核心内容:聊天窗口。 不过在实现窗口时,我没有把所有逻辑都写进一个文件里,而是提前拆成了几个层次: ui:负责界面显示。 controllers:负责聊天流程。 llm:负责大模型接口。 storage:负责 SQLite 数据读写。 service...
决策树:像做选择题一样理解分类与回归
决策树(Decision Tree)是一种非常符合人类直觉的机器学习算法。它的预测过程就像做选择题:先问一个问题,根据答案走到下一步,再继续问问题,直到得到最终结论。 例如判断一个学生考试是否通过,可以问: 学习时间是否大于 3 小时? 睡眠时间是否大于 7 小时? 是否完成复习? 最后走到某个叶子节点,得到“通过”或“不通过”的预测结果。 1. 决策树的基本思想决策树的核心思想是:不断选择一个最合适的特征,把数据集切分得越来越“纯”。 这里的“纯”可以理解为:一个节点里的样本类别越统一,就越纯。 例如一个节点里有 10 个样本: 如果 10 个都是“通过”,这个节点非常纯。 如果 5 个“通过”、5 个“不通过”,这个节点就很混乱。 训练决策树时,算法会不断寻找最佳划分条件,例如: 学习时间 >= 3 小时? 如果这个问题能把“通过”和“不通过”分得更开,就说明它是一个不错的划分。 2. 决策树由哪些部分组成一棵决策树通常包含三类节点: 结构 含义 根节点 整棵树的起点,包含全部训练数据 内部节点 一个判断条件,例如“年龄是否大于 18” 叶...
Hexo 部署到 GitHub Pages 后构建失败:从 .nojekyll 到 ignore_hidden 的完整排查
最近在使用 Hexo 部署博客时遇到了一个问题:本地执行 npx hexo d 看起来是成功的,但 GitHub Pages 的 pages build and deployment 工作流却失败了。 一开始我以为是 Hexo 生成出错,后来排查发现,真正的问题并不在文章内容,也不在本地生成,而是在 GitHub Pages 构建流程和 .nojekyll 文件没有真正部署到公开仓库之间。 这篇文章记录一下完整排查过程。 1. 问题现象(若想直接看如何解决可以跳转到7.最终解决)本地执行: npx hexo d 命令看起来可以正常执行,静态文件也会被推送到 GitHub Pages 仓库。 但是在 GitHub 仓库的 Actions 页面中,可以看到 pages build and deployment 失败,具体失败位置在 build 步骤,错误信息中出现了: Failed to download archive 'https://codeload.github.com/actions/jekyll-build-pages/...' An action could ...
AICompanion 开发日志 01:项目搭建与环境准备
最近我开始做一个新的项目:AICompanion。它的目标是做一个运行在 Windows 电脑桌面上的 AI 聊天陪伴系统,未来也可能扩展到安卓端。 这个项目不只是普通聊天窗口,而是希望逐步实现一个更像“桌面伙伴”的系统:它可以拥有动漫角色设定,能记住聊天内容,有情绪状态,在我长时间没有回复时主动发消息给我,后续再加入 TTS 语音回复和 Live2D 角色展示。 这篇文章是这个系列的第 1 篇,主要记录项目从 0 到 1 的环境搭建和基础结构设计。 1. 项目目标AICompanion 当前的核心目标可以拆成几部分: 使用 PySide6 开发 Windows 桌面聊天窗口。 通过 API 调用大模型,实现多轮对话。 使用 SQLite 保存聊天记录、长期记忆和任务提醒。 加入情感系统,让角色回复不只是机械问答。 支持主动消息,例如用户长时间没回复时,角色可以主动问候。 后续加入 TTS 语音回复。 最终尝试接入 Live2D,让动漫角色可以根据情绪切换表情和动作。 我希望它最终不是一个“输入框 + 回复框”的简单工具,而是一个有角色感、有陪伴感、能长期相处的桌面 AI...
梯度下降法:机器学习如何一步步找到最优解
在机器学习中,我们经常会说一句话:模型训练的目标,是让损失函数尽可能小(在深度学习中也经常使用梯度下降)。 但问题是:参数那么多,损失函数又可能很复杂,模型到底应该怎样找到那个“比较好”的参数呢? 梯度下降法就是最常用的答案之一。它的思想并不神秘:先站在一个位置,看哪里下降最快,然后朝那个方向走一小步;重复很多次以后,就逐渐接近损失函数的低谷(梯度下降就是通过多元求极值的方式来求最小值)。 1. 为什么需要梯度下降以线性回归为例,模型可以写成: $$\hat{y} = wx + b$$ 其中: $x$ 是输入特征。 $\hat{y}$ 是模型预测值。 $w$ 是权重。 $b$ 是偏置。 我们希望预测值 $\hat{y}$ 尽量接近真实值 $y$,因此可以使用均方误差作为损失函数: $$J(w, b) = \frac{1}{m}\sum_{i=1}^{m}(\hat{y}^{(i)} - y^{(i)})^2$$ 因为 $\hat{y}^{(i)} = wx^{(i)} + b$,所以也可以写成: $$J(w, b) = \...
交叉验证与网格搜索:让模型选择更可靠
训练机器学习模型时,我们经常会遇到两个问题: 这个模型到底是不是真的好? 参数应该怎么选,比如 KNN 的 $K$、逻辑回归的正则化强度、决策树的最大深度? 如果只把数据随便分成一次训练集和测试集,然后看一次准确率,很容易被偶然性误导。交叉验证(Cross Validation)就是为了解决“评估不稳定”的问题;网格搜索(Grid Search)则是为了解决“超参数怎么选”的问题。 1. 为什么不能只划分一次数据假设我们有一份数据集,要判断一个模型效果如何。最常见的做法是: 训练集:用来训练模型 测试集:用来评估模型 这听起来很合理,但有一个问题:一次划分可能有运气成分。 如果测试集刚好比较简单,模型分数会偏高;如果测试集刚好比较困难,模型分数会偏低。也就是说,一次测试结果可能不能代表模型的真实水平。 可以把它想象成考试: 只考一张卷子,可能刚好考到你熟悉的题。 多考几张不同卷子,平均成绩才更接近真实水平。 交叉验证的思想就是:让模型多考几次,再看平均表现。 2. 什么是 K 折交叉验证K 折交叉验证(K-Fold Cross Validation)会把数据分成 ...
Hexo 如何实现源码私有、页面公开部署
使用 Hexo 搭建博客时,我们通常会接触到两类文件: Hexo 源代码:Markdown 文章、主题配置、站点配置、脚手架、依赖文件等。 静态网页文件:Hexo 生成后的 HTML、CSS、JS、图片等,也就是访问者真正看到的网页。 如果把这两类文件都放在同一个公开仓库里,别人不仅能看到博客页面,也能看到你的 Markdown 原文、草稿结构、主题配置和提交历史。为了更好地管理博客,可以采用一种更清晰的方案:源码放私有仓库,网页放公开仓库。 1. 为什么要分开部署Hexo 的工作流程可以理解为: Hexo 源代码 | | npx hexo generate v 静态网页文件 | | npx hexo deploy v GitHub Pages 公开访问 访问者真正需要的是生成后的静态网页,并不需要看到你的 Hexo 源代码。 因此,更合理的结构是: 私有仓库:保存 Hexo 源代码 公开仓库:保存 hexo deploy 生成的网页文件 这样既能正常公开博客,又能避免源码仓库暴露。 2. public 和 privac...
逻辑回归:从直线到概率的分类算法
逻辑回归(Logistic Regression)名字里有“回归”,但它最常用于分类任务,尤其是二分类问题(逻辑回归说白了就是在线性回归的基础上使用Sigmoid 函数将原先结果转为概率的形式,然后根据设定的阈值来判断是否是正类)。 它要回答的问题不是“这个样本的数值是多少”,而是“这个样本属于某一类的概率有多大”。例如: 一封邮件是垃圾邮件的概率是多少? 一个用户会不会点击广告? 一个肿瘤样本是恶性的概率是多少? 一个学生是否能通过考试? 逻辑回归的核心思想可以概括为一句话:先用一条直线算出一个分数,再用 Sigmoid 函数把这个分数压缩成 $0$ 到 $1$ 之间的概率。 1. 从线性回归说起在线性回归中,我们用一个线性函数预测连续值: $$z = wx + b$$ 如果是多个特征(w可以看作权重),可以写成: $$z = \mathbf{w}^{T}\mathbf{x} + b$$ 这里的 $z$ 可以理解为模型给样本打出的“原始分数”。 但是分类问题不能直接使用这个分数。比如判断一封邮件是不是垃圾邮件时,我们希望模型输出的是: $$P(y&...
