机器学习模型评估指标详解:准确率、召回率与F1
训练机器学习模型后,必须回答一个问题:模型到底好不好?很多人第一反应是看准确率,但准确率并不总是可靠。尤其在类别不均衡、漏判成本高或误判成本高的业务中,只看准确率可能得到完全错误的结论。 本文介绍分类任务中常用指标:准确率、精确率、召回率、F1、混淆矩阵,并结合 Scikit-learn 给出计算方式。 混淆矩阵以二分类为例,模型预测结果可以分为四类: TP:真实为正,预测为正。 TN:真实为负,预测为负。 FP:真实为负,预测为正。 FN:真实为正,预测为负。 例如垃圾邮件识别中,垃圾邮件是正类。把垃圾邮件识别为垃圾邮件是 TP;把正常邮件误判为垃圾邮件是 FP;把垃圾邮件漏掉是 FN。 Scikit-learn 可以直接计算混淆矩阵: 1234567from sklearn.metrics import confusion_matrixy_true = [1, 0, 1, 1, 0, 0, 1]y_pred = [1, 0, 0, 1, 0, 1, 1]matrix = confusion_matrix(y_true,...
PyTorch训练过程中的过拟合判断与处理
训练深度学习模型时,过拟合是最常见的问题之一。模型在训练集上表现很好,但在验证集或真实数据上效果明显变差。它不是 PyTorch 独有的问题,而是机器学习模型容量、数据规模、训练策略和任务复杂度共同作用的结果。 本文以 PyTorch 训练流程为例,说明如何判断过拟合,以及常用处理手段:数据划分、监控曲线、正则化、Dropout、数据增强、早停和模型简化。 什么是过拟合过拟合可以简单理解为模型“记住了训练样本”,但没有学到足够通用的规律。典型表现是: 训练 loss 持续下降。 训练 accuracy 持续上升。 验证 loss 下降一段时间后开始上升。 验证 accuracy 停滞甚至下降。 只看训练集指标很容易误判。一个模型训练准确率 99%,并不代表它在新数据上也能达到同样效果。 正确划分数据集判断过拟合的前提是有独立验证集: 1234567891011from torch.utils.data import random_splitdataset = MyDataset()train_size = int(len(dataset) * 0.8)val_size =...
Python异步任务队列的简单实现
在 Python 项目中,经常会遇到“请求进来后不想立即处理完所有工作”的场景。例如发送通知、同步第三方数据、生成报表、处理图片、写入慢速日志等。如果这些耗时操作都放在主请求链路里,接口响应会变慢,也更容易因为外部依赖波动而失败。 对于大型系统,可以使用 Celery、RQ、Kafka 或云消息队列。但在小型服务或内部工具里,有时只需要一个进程内的异步任务队列。本文使用 asyncio.Queue 实现一个简单版本。 适用场景和边界进程内队列适合: 任务量较小。 允许服务重启后丢失未完成任务。 单进程或少量进程部署。 主要目的是削峰和解耦请求耗时。 它不适合: 订单支付、扣库存等必须可靠执行的任务。 多实例之间需要共享任务。 需要失败重试、延迟任务、任务状态查询的复杂场景。 如果任务不能丢,应该使用外部消息队列或数据库任务表。 定义任务结构先定义一个任务对象: 123456789from dataclasses import dataclass, fieldfrom typing import Anyimport time@dataclassclass Task: ...
FastAPI接口参数校验与异常处理
FastAPI 的一个重要优势是基于类型标注和 Pydantic 做参数校验。接口参数写清楚后,框架可以自动解析请求、校验字段、生成 OpenAPI 文档,并在参数错误时返回结构化响应。 本文通过一个用户创建接口,介绍路径参数、查询参数、请求体校验、自定义异常和统一错误返回的实践方式。 基础项目结构一个简单项目可以这样组织: 1234app/ main.py schemas.py exceptions.py 安装依赖: 1pip install fastapi uvicorn 启动命令: 1uvicorn app.main:app --reload 访问 http://127.0.0.1:8000/docs 可以查看自动生成的接口文档。 定义请求体模型在 schemas.py 中定义用户创建请求: 123456from pydantic import BaseModel, EmailStr, Fieldclass CreateUserRequest(BaseModel): username: str = Field(min_length=3,...
Python虚拟环境与依赖管理实践
Python 项目最常见的问题之一是依赖混乱:本地能运行,服务器不能运行;A 项目升级了某个库,B 项目突然报错;系统 Python 被安装了大量第三方包,最后不敢删除也不敢升级。 解决这类问题的基础做法是使用虚拟环境,并把依赖版本记录下来。本文以标准库 venv 和 pip 为主,介绍一套简单可靠的依赖管理流程。 为什么需要虚拟环境虚拟环境会为每个项目创建独立的 Python 包安装目录。项目安装的第三方库只影响当前环境,不污染系统 Python,也不会影响其他项目。 适合使用虚拟环境的场景包括: Web 服务项目。 数据处理脚本。 机器学习实验。 定时任务。 需要固定版本依赖的旧项目。 即使只是一个小脚本,只要安装了第三方库,也建议创建虚拟环境。 创建虚拟环境在项目根目录执行: 1python3 -m venv .venv 激活环境: 1source .venv/bin/activate 激活后终端前面通常会出现 (.venv)。此时执行: 12which pythonwhich pip 路径应该指向当前项目的 .venv 目录。 Windows...
Python日志系统logging实战配置
日志是排查线上问题时最重要的信息来源之一。Python 标准库自带 logging 模块,功能足够支撑大部分脚本、Web 服务和后台任务。很多项目一开始直接使用 print,等任务跑到服务器上才发现无法区分级别、没有时间、没有模块名,也不好重定向到文件。 本文介绍一套适合中小型 Python 项目的 logging 配置方式,包括控制台输出、文件轮转、模块 logger、异常堆栈和常见坑。 logging 的基本概念logging 主要包含四个对象: Logger:代码中使用的日志入口。 Handler:决定日志输出到哪里,例如控制台、文件、网络。 Formatter:决定日志格式。 Level:决定哪些日志会被输出,例如 DEBUG、INFO、WARNING、ERROR。 在业务代码中不要直接操作 root logger,推荐为每个模块创建自己的 logger: 123456import logginglogger = logging.getLogger(__name__)def run(): logger.info("task...
Go项目配置管理:环境变量与配置文件实践
配置管理是后端项目里很基础但容易混乱的部分。服务端程序通常需要数据库地址、Redis 地址、日志级别、HTTP 端口、第三方接口密钥等配置。如果这些内容散落在代码里,部署到测试、预发和生产环境时就会变得难以维护。 本文介绍一种简单、可落地的 Go 配置管理方式:默认值放代码,环境相关配置通过配置文件或环境变量覆盖,敏感信息优先使用环境变量。 配置应该解决的问题一个可维护的配置方案至少要满足以下要求: 本地开发可以快速启动。 测试、预发、生产环境可以使用不同配置。 敏感信息不提交到 Git。 程序启动时能尽早发现缺失或非法配置。 配置结构和业务模块对应,便于理解。 不要把配置系统做得过重。对于中小型服务,结构体加 JSON、YAML 或环境变量已经足够。 定义配置结构体先定义一个结构体描述服务需要的配置: 1234567891011121314151617181920212223type Config struct { App AppConfig `json:"app"` HTTP HTTPConfig ...
Go实现一个简单的HTTP中间件链
HTTP 中间件是 Web 服务里非常常见的设计。日志、鉴权、跨域、压缩、panic 恢复、限流、请求追踪都可以通过中间件实现。理解中间件链之后,即使不使用大型 Web 框架,也能基于标准库写出清晰的服务结构。 本文使用 Go 标准库的 net/http 实现一个简单中间件链,并说明执行顺序、错误处理和扩展方式。 中间件的本质在标准库里,一个 HTTP 处理器只要实现 http.Handler 接口即可: 123type Handler interface { ServeHTTP(ResponseWriter, *Request)} 中间件可以理解为一个函数:接收一个 http.Handler,返回一个新的 http.Handler。 1type Middleware func(http.Handler) http.Handler 这样就可以在真正的业务 handler 前后增加逻辑。 编写日志中间件先实现一个简单的访问日志中间件: 123456789func Logger(next http.Handler) http.Handler...
Go错误处理最佳实践:从panic到errors.Is
Go 的错误处理看起来很朴素:函数返回 error,调用方判断是否为 nil。这种方式没有异常机制那么“自动”,但优点是错误路径非常明确。项目规模变大后,真正的难点不是写 if err != nil,而是如何保留上下文、如何判断错误类型、什么时候使用 panic,以及如何让日志对排查问题有帮助。 error 的基本约定Go 中的错误是一种普通值。一个函数如果可能失败,通常把 error 放在最后一个返回值: 12345678910111213func LoadConfig(path string) (*Config, error) { data, err := os.ReadFile(path) if err != nil { return nil, err } var cfg Config if err := json.Unmarshal(data, &cfg); err != nil { return nil, err } return &cfg, nil} 调用方必须根据 err...
Go并发编程中的context使用实践
在 Go 服务端开发中,context 经常和 HTTP 请求、数据库查询、RPC 调用、后台 goroutine 一起出现。很多初学者知道它可以“传递上下文”,但在实际项目里更重要的作用是控制超时、取消任务和传递请求级元数据。 如果没有统一的取消机制,一个请求已经断开,后面的数据库查询、第三方接口调用和 goroutine 仍然可能继续运行。并发量上来之后,这类问题会表现为连接池耗尽、CPU 空转、日志堆积和接口尾延迟变大。 context 解决什么问题context.Context 主要用于跨 API 边界传递三个信息: 取消信号:上游不再需要结果时,下游应尽快停止。 截止时间:任务必须在某个时间点前结束。 请求级数据:例如 trace id、用户 id、语言环境等轻量信息。 它不是全局变量,也不是业务参数容器。对于订单号、分页参数、查询条件这类业务数据,应该继续通过普通函数参数传递。 基础用法最常见的是从 context.Background()...




