当前位置: 首页 > news >正文

你的第一个AI项目部署:用Flask快速搭建模型推理API

点击AladdinEdu,同学们用得起的【H卡】算力平台”,注册即送-H卡级别算力80G大显存按量计费灵活弹性顶级配置学生更享专属优惠


引言:从模型训练到实际应用的桥梁

在机器学习项目的完整生命周期中,模型训练只是完成了前半部分工作。将训练好的模型部署为可用的服务,使其能够处理真实世界的请求,才是创造实际价值的关键步骤。Flask作为一个轻量级的Python Web框架,是快速搭建模型推理API的理想选择。

本文将带你完整经历从模型准备到API部署的全过程,通过一个情感分析模型的实例,展示如何构建健壮、高效的机器学习推理服务。无论你是机器学习初学者还是有一定经验的开发者,都能从中获得模型部署的实战经验。

1. 环境准备与项目规划

1.1 技术栈选择

核心框架

  • Flask:轻量级Web框架,API开发首选
  • PyTorch/TensorFlow:模型推理框架
  • Gunicorn:生产环境WSGI服务器
  • Docker:容器化部署

辅助工具

  • Swagger/OpenAPI:API文档生成
  • Postman:API测试工具
  • Prometheus/Grafana:监控指标可视化

1.2 项目结构设计

在开始编码前,先规划好项目结构:

ml-api-project/
├── app/
│   ├── __init__.py
│   ├── main.py          # Flask应用主文件
│   ├── models/          # 模型相关代码
│   │   ├── __init__.py
│   │   ├── model_loader.py  # 模型加载器
│   │   └── sentiment_model.py  # 情感分析模型
│   ├── utils/           # 工具函数
│   │   ├── __init__.py
│   │   ├── preprocess.py  # 数据预处理
│   │   └── validation.py  # 输入验证
│   ├── api/            # API路由
│   │   ├── __init__.py
│   │   ├── endpoints.py  # API端点
│   │   └── schemas.py    # API数据结构
│   ├── static/         # 静态文件
│   └── templates/      # 模板文件
├── tests/              # 测试代码
├── config/             # 配置文件
├── requirements.txt    # 依赖列表
├── Dockerfile         # Docker配置
├── docker-compose.yml # Docker编排
└── README.md          # 项目说明

1.3 环境配置

创建并激活虚拟环境:

# 创建虚拟环境
python -m venv venv# 激活虚拟环境(Linux/Mac)
source venv/bin/activate# 激活虚拟环境(Windows)
venv\Scripts\activate# 安装基础依赖
pip install flask torch transformers numpy pandas

2. Flask基础与应用搭建

2.1 最小Flask应用

首先创建一个最简单的Flask应用来理解基本概念:

# app/main.py
from flask import Flask, jsonifydef create_app():"""创建Flask应用工厂函数"""app = Flask(__name__)# 基础配置app.config['JSON_AS_ASCII'] = False  # 支持中文app.config['DEBUG'] = True  # 开发模式@app.route('/health', methods=['GET'])def health_check():"""健康检查端点"""return jsonify({'status': 'healthy','message': 'API服务运行正常'})@app.route('/')def home():"""首页"""return jsonify({'message': '欢迎使用情感分析API','endpoints': {'health_check': '/health','sentiment_analysis': '/api/v1/sentiment'}})return appif __name__ == '__main__':app = create_app()app.run(host='0.0.0.0', port=5000)

运行应用:

python app/main.py

访问 http://localhost:5000/health 应该能看到健康检查响应。

2.2 配置管理

创建配置文件,区分开发和生产环境:

# config/development.py
class DevelopmentConfig:DEBUG = TrueTESTING = FalseMODEL_PATH = 'models/sentiment_model'MAX_CONTENT_LENGTH = 16 * 1024 * 1024  # 16MB最大请求大小# config/production.py
class ProductionConfig:DEBUG = FalseTESTING = FalseMODEL_PATH = '/app/models/sentiment_model'MAX_CONTENT_LENGTH = 16 * 1024 * 1024# 生产环境特定配置PREFERRED_URL_SCHEME = 'https'

3. 模型准备与加载

3.1 情感分析模型选择

我们使用Hugging Face的Transformers库中的预训练情感分析模型:

# app/models/sentiment_model.py
from transformers import AutoTokenizer, AutoModelForSequenceClassification
from transformers import pipeline
import torchclass SentimentAnalyzer:"""情感分析模型封装类"""def __init__(self, model_name="cardiffnlp/twitter-roberta-base-sentiment"):self.model_name = model_nameself.device = 0 if torch.cuda.is_available() else -1self.classifier = Noneself.labels = ["negative", "neutral", "positive"]def load_model(self):"""加载模型"""try:self.classifier = pipeline("sentiment-analysis",model=self.model_name,device=self.device,return_all_scores=True)print(f"模型加载成功,使用设备: {'GPU' if self.device == 0 else 'CPU'}")except Exception as e:print(f"模型加载失败: {str(e)}")raisedef predict(self, text):"""预测文本情感"""if not self.classifier:raise ValueError("模型未加载,请先调用load_model()")if not text or not isinstance(text, str):raise ValueError("输入文本不能为空且必须是字符串")# 限制文本长度text = text[:512]  # 模型最大输入长度try:results = self.classifier(text)return self._format_results(results[0])except Exception as e:print(f"预测失败: {str(e)}")raisedef _format_results(self, results):"""格式化预测结果"""formatted = []for result in results:formatted.append({'label': result['label'],'score': float(result['score']),'sentiment': self._map_sentiment(result['label'])})# 按置信度排序formatted.sort(key=lambda x: x['score'], reverse=True)return formatteddef _map_sentiment(self, label):"""映射情感标签到中文"""mapping = {'LABEL_0': '负面','LABEL_1': '中性', 'LABEL_2': '正面'}return mapping.get(label, '未知')# 模型加载器单例
class ModelLoader:_instance = Nonedef __new__(cls):if cls._instance is None:cls._instance = super().__new__(cls)cls._instance.models = {}return cls._instancedef get_sentiment_model(self):"""获取情感分析模型实例"""if 'sentiment' not in self.models:model = SentimentAnalyzer()model.load_model()self.models['sentiment'] = modelreturn self.models['sentiment']

3.2 模型预热与缓存

# app/utils/cache.py
from functools import lru_cache
import timeclass ModelCache:"""模型预测结果缓存"""def __init__(self, max_size=1000, ttl=300):self.cache = {}self.max_size = max_sizeself.ttl = ttl  # 缓存有效期(秒)@lru_cache(maxsize=1000)def cached_predict(self, text, model_name):"""带缓存的预测"""# 实际预测逻辑model_loader = ModelLoader()model = model_loader.get_model(model_name)return model.predict(text)def clear_expired(self):"""清理过期缓存"""current_time = time.time()expired_keys = [key for key, (_, timestamp) in self.cache.items()if current_time - timestamp > self.ttl]for key in expired_keys:del self.cache[key]# 全局缓存实例
model_cache = ModelCache()

4. API设计与实现

4.1 数据验证与序列化

使用Marshmallow进行数据验证:

# app/api/schemas.py
from marshmallow import Schema, fields, validate, validates_schema, ValidationErrorclass SentimentRequestSchema(Schema):"""情感分析请求 schema"""text = fields.Str(required=True,validate=validate.Length(min=1, max=1000),metadata={'description': '需要分析情感的文本'})language = fields.Str(load_default='zh',validate=validate.OneOf(['zh', 'en']),metadata={'description': '文本语言'})@validates_schemadef validate_text_content(self, data, **kwargs):"""验证文本内容"""text = data.get('text', '')if not text.strip():raise ValidationError('文本内容不能为空')class SentimentResponseSchema(Schema):"""情感分析响应 schema"""success = fields.Bool(metadata={'description': '请求是否成功'})predictions = fields.List(fields.Dict(), metadata={'description': '预测结果'})processing_time = fields.Float(metadata={'description': '处理时间(秒)'})model_version = fields.Str(metadata={'description': '模型版本'})# 初始化schema实例
sentiment_request_schema = SentimentRequestSchema()
sentiment_response_schema = SentimentResponseSchema()

4.2 API端点实现

# app/api/endpoints.py
from flask import Blueprint, request, jsonify
from app.api.schemas import sentiment_request_schema, sentiment_response_schema
from app.models.sentiment_model import ModelLoader
import time# 创建蓝图
api_bp = Blueprint('api', __name__, url_prefix='/api/v1')@api_bp.route('/sentiment', methods=['POST'])
def analyze_sentiment():"""情感分析端点---tags:- 情感分析description: 分析文本的情感倾向parameters:- in: bodyname: bodyschema:type: objectrequired:- textproperties:text:type: stringdescription: 需要分析的文本language:type: stringdescription: 文本语言enum: [zh, en]default: zhresponses:200:description: 分析成功schema:type: objectproperties:success:type: booleanpredictions:type: arrayitems:type: objectproperties:label:type: stringscore:type: numbersentiment:type: stringprocessing_time:type: numbermodel_version:type: string400:description: 请求参数错误500:description: 服务器内部错误"""start_time = time.time()try:# 验证请求数据data = sentiment_request_schema.load(request.json)# 获取模型实例model_loader = ModelLoader()model = model_loader.get_sentiment_model()# 进行预测predictions = model.predict(data['text'])# 构建响应response_data = {'success': True,'predictions': predictions,'processing_time': round(time.time() - start_time, 4),'model_version': model.model_name}return sentiment_response_schema.dump(response_data), 200except Exception as e:error_response = {'success': False,'error': str(e),'processing_time': round(time.time() - start_time, 4)}return jsonify(error_response), 400@api_bp.route('/batch-sentiment', methods=['POST'])
def batch_analyze_sentiment():"""批量情感分析端点---tags:- 情感分析description: 批量分析文本的情感倾向"""# 实现批量处理逻辑pass

4.3 应用工厂模式

更新主应用文件以使用工厂模式:

# app/main.py
from flask import Flask, jsonify
from app.api.endpoints import api_bp
from app.models.sentiment_model import ModelLoaderdef create_app(config_class='config.development.DevelopmentConfig'):"""应用工厂函数"""app = Flask(__name__)# 加载配置app.config.from_object(config_class)# 注册蓝图app.register_blueprint(api_bp)# 初始化模型@app.before_first_requestdef initialize_models():"""在第一个请求前初始化模型"""try:model_loader = ModelLoader()model_loader.get_sentiment_model()  # 预热模型print("模型初始化完成")except Exception as e:print(f"模型初始化失败: {str(e)}")# 错误处理@app.errorhandler(404)def not_found(error):return jsonify({'error': '端点不存在'}), 404@app.errorhandler(500)def internal_error(error):return jsonify({'error': '服务器内部错误'}), 500return appif __name__ == '__main__':app = create_app()app.run(host='0.0.0.0', port=5000)

5. 高级功能与优化

5.1 请求限流与保护

# app/utils/rate_limit.py
from flask import request
from functools import wraps
import timeclass RateLimiter:"""简单的请求限流器"""def __init__(self, max_requests=100, per_second=1):self.requests = {}self.max_requests = max_requestsself.per_second = per_seconddef is_rate_limited(self, client_id):"""检查是否应该限流"""current_time = time.time()if client_id not in self.requests:self.requests[client_id] = []# 清理过期请求记录self.requests[client_id] = [t for t in self.requests[client_id] if current_time - t < 60  # 保留60秒内的记录]# 检查请求频率if len(self.requests[client_id]) >= self.max_requests:return True# 记录本次请求self.requests[client_id].append(current_time)return False# 全局限流器实例
rate_limiter = RateLimiter(max_requests=100, per_second=1)def rate_limit(f):"""限流装饰器"""@wraps(f)def decorated_function(*args, **kwargs):client_id = request.remote_addr  # 使用IP作为客户端标识if rate_limiter.is_rate_limited(client_id):return jsonify({'error': '请求频率过高','message': '请稍后再试'}), 429return f(*args, **kwargs)return decorated_function# 在API端点中使用
@api_bp.route('/sentiment', methods=['POST'])
@rate_limit
def analyze_sentiment():# 原有逻辑pass

5.2 监控与日志

# app/utils/monitoring.py
import time
from flask import request
from prometheus_client import Counter, Histogram, generate_latest# 定义监控指标
REQUEST_COUNT = Counter('http_requests_total','Total HTTP Requests',['method', 'endpoint', 'http_status']
)REQUEST_LATENCY = Histogram('http_request_duration_seconds','HTTP request latency',['method', 'endpoint']
)def setup_metrics(app):"""设置应用监控"""@app.before_requestdef before_request():"""请求开始前记录时间"""request.start_time = time.time()@app.after_requestdef after_request(response):"""请求结束后记录指标"""# 计算请求耗时latency = time.time() - request.start_time# 记录指标REQUEST_COUNT.labels(method=request.method,endpoint=request.path,http_status=response.status_code).inc()REQUEST_LATENCY.labels(method=request.method,endpoint=request.path).observe(latency)return response@app.route('/metrics')def metrics():"""暴露Prometheus指标"""return generate_latest(), 200, {'Content-Type': 'text/plain'}

5.3 异步处理支持

对于耗时的预测任务,可以使用异步处理:

# app/utils/async_processing.py
from concurrent.futures import ThreadPoolExecutor
from flask import current_app# 全局线程池
executor = ThreadPoolExecutor(max_workers=4)def async_predict(text, callback):"""异步预测"""def run_prediction():try:with current_app.app_context():model_loader = ModelLoader()model = model_loader.get_sentiment_model()result = model.predict(text)callback(result, None)except Exception as e:callback(None, str(e))executor.submit(run_prediction)# 异步API端点
@api_bp.route('/async-sentiment', methods=['POST'])
def async_analyze_sentiment():"""异步情感分析"""data = sentiment_request_schema.load(request.json)def callback(result, error):if error:# 处理错误print(f"异步处理错误: {error}")else:# 处理结果,可以存储到数据库或发送消息print(f"异步处理结果: {result}")# 提交异步任务async_predict(data['text'], callback)return jsonify({'success': True,'message': '请求已接收,正在处理中','task_id': 'some_task_id'  # 实际项目中应该返回任务ID}), 202

6. 测试与验证

6.1 单元测试

# tests/test_api.py
import pytest
from app import create_app
from app.models.sentiment_model import SentimentAnalyzer@pytest.fixture
def app():"""创建测试应用"""app = create_app('config.testing.TestingConfig')yield app@pytest.fixture
def client(app):"""创建测试客户端"""return app.test_client()def test_health_check(client):"""测试健康检查端点"""response = client.get('/health')assert response.status_code == 200assert response.json['status'] == 'healthy'def test_sentiment_analysis(client):"""测试情感分析端点"""test_data = {'text': '我非常喜欢这个产品,质量很好!','language': 'zh'}response = client.post('/api/v1/sentiment', json=test_data)assert response.status_code == 200assert response.json['success'] == Trueassert 'predictions' in response.jsondef test_invalid_input(client):"""测试无效输入"""test_data = {'text': '',  # 空文本'language': 'zh'}response = client.post('/api/v1/sentiment', json=test_data)assert response.status_code == 400assert response.json['success'] == Falsedef test_model_loading():"""测试模型加载"""model = SentimentAnalyzer()model.load_model()assert model.classifier is not None# 运行测试
if __name__ == '__main__':pytest.main([__file__])

6.2 API测试脚本

# scripts/test_api.py
import requests
import json
import timedef test_api_performance():"""测试API性能"""base_url = "http://localhost:5000"# 测试数据test_cases = ["这个产品太棒了!","我不太满意这次的服务","中规中矩,没什么特别的感觉","非常糟糕的体验,再也不会购买了"]total_time = 0successful_requests = 0for i, text in enumerate(test_cases, 1):payload = {"text": text,"language": "zh"}start_time = time.time()try:response = requests.post(f"{base_url}/api/v1/sentiment",json=payload,timeout=10)end_time = time.time()if response.status_code == 200:successful_requests += 1processing_time = response.json()['processing_time']total_time += processing_timeprint(f"请求 {i}: 成功 - 处理时间: {processing_time:.4f}s")else:print(f"请求 {i}: 失败 - 状态码: {response.status_code}")except Exception as e:print(f"请求 {i}: 异常 - {str(e)}")# 输出性能报告if successful_requests > 0:avg_time = total_time / successful_requestsprint(f"\n性能报告:")print(f"总请求数: {len(test_cases)}")print(f"成功请求: {successful_requests}")print(f"平均处理时间: {avg_time:.4f}s")print(f"QPS: {1/avg_time:.2f}")if __name__ == "__main__":test_api_performance()

7. 生产环境部署

7.1 使用Gunicorn部署

创建Gunicorn配置文件:

# gunicorn_config.py
import multiprocessing# 工作进程数
workers = multiprocessing.cpu_count() * 2 + 1# 绑定地址
bind = "0.0.0.0:5000"# 工作模式
worker_class = "sync"# 最大请求数
max_requests = 1000# 超时时间
timeout = 30# 访问日志
accesslog = "-"# 错误日志
errorlog = "-"# 日志级别
loglevel = "info"

启动命令:

gunicorn -c gunicorn_config.py "app.main:create_app()"

7.2 Docker容器化

创建Dockerfile:

# Dockerfile
FROM python:3.9-slim# 设置工作目录
WORKDIR /app# 安装系统依赖
RUN apt-get update && apt-get install -y \gcc \&& rm -rf /var/lib/apt/lists/*# 复制依赖文件
COPY requirements.txt .# 安装Python依赖
RUN pip install --no-cache-dir -r requirements.txt# 复制应用代码
COPY . .# 创建非root用户
RUN useradd -m -u 1000 webuser
USER webuser# 暴露端口
EXPOSE 5000# 启动命令
CMD ["gunicorn", "-c", "gunicorn_config.py", "app.main:create_app()"]

创建docker-compose.yml:

# docker-compose.yml
version: '3.8'services:ml-api:build: .ports:- "5000:5000"environment:- FLASK_ENV=production- MODEL_PATH=/app/modelsvolumes:- ./models:/app/modelsrestart: unless-stoppedhealthcheck:test: ["CMD", "curl", "-f", "http://localhost:5000/health"]interval: 30stimeout: 10sretries: 3# 可以添加其他服务,如Redis、数据库等

7.3 Nginx反向代理配置

# nginx.conf
server {listen 80;server_name your-domain.com;# 静态文件服务location /static {alias /app/static;expires 30d;}# API反向代理location / {proxy_pass http://localhost:5000;proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_set_header X-Forwarded-Proto $scheme;# 超时设置proxy_connect_timeout 30s;proxy_send_timeout 30s;proxy_read_timeout 30s;}# 限制请求大小client_max_body_size 10M;
}

8. 监控与维护

8.1 健康检查端点

# app/api/health.py
from flask import Blueprint, jsonify
import psutil
import torchhealth_bp = Blueprint('health', __name__)@health_bp.route('/health/detailed', methods=['GET'])
def detailed_health():"""详细健康检查"""# 系统信息memory = psutil.virtual_memory()disk = psutil.disk_usage('/')# GPU信息(如果可用)gpu_info = {}if torch.cuda.is_available():gpu_info = {'gpu_available': True,'gpu_count': torch.cuda.device_count(),'gpu_memory': [torch.cuda.get_device_properties(i).total_memory for i in range(torch.cuda.device_count())]}else:gpu_info = {'gpu_available': False}return jsonify({'status': 'healthy','system': {'memory_total': memory.total,'memory_available': memory.available,'disk_total': disk.total,'disk_free': disk.free,'cpu_percent': psutil.cpu_percent()},'gpu': gpu_info,'model_loaded': ModelLoader().is_model_loaded('sentiment')})

8.2 日志配置

# app/utils/logging.py
import logging
from logging.handlers import RotatingFileHandler
import osdef setup_logging(app):"""配置应用日志"""if not os.path.exists('logs'):os.makedirs('logs')# 文件日志处理器file_handler = RotatingFileHandler('logs/ml-api.log',maxBytes=1024 * 1024 * 10,  # 10MBbackupCount=10)file_handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'))file_handler.setLevel(logging.INFO)# 应用日志配置app.logger.addHandler(file_handler)app.logger.setLevel(logging.INFO)app.logger.info('ML API启动成功')

9. 安全考虑

9.1 输入验证与清理

# app/utils/security.py
import html
import redef sanitize_input(text):"""清理用户输入"""if not isinstance(text, str):return ""# 移除HTML标签text = re.sub(r'<[^>]*>', '', text)# HTML转义text = html.escape(text)# 限制长度text = text[:1000]return textdef validate_text_content(text):"""验证文本内容"""# 检查空文本if not text or not text.strip():return False, "文本内容不能为空"# 检查过长文本if len(text) > 1000:return False, "文本长度不能超过1000个字符"# 检查可疑内容(简单示例)suspicious_patterns = [r'(http|https)://',r'<script>',r'javascript:']for pattern in suspicious_patterns:if re.search(pattern, text, re.IGNORECASE):return False, "文本包含可疑内容"return True, ""

9.2 API密钥认证

# app/utils/auth.py
from functools import wraps
from flask import request, jsonify
import osdef require_api_key(f):"""API密钥认证装饰器"""@wraps(f)def decorated_function(*args, **kwargs):api_key = request.headers.get('X-API-Key')if not api_key or api_key != os.getenv('API_KEY'):return jsonify({'error': '无效的API密钥','message': '请提供有效的API密钥'}), 401return f(*args, **kwargs)return decorated_function# 在需要认证的端点中使用
@api_bp.route('/secure-sentiment', methods=['POST'])
@require_api_key
def secure_analyze_sentiment():# 需要API密钥的情感分析pass

10. 总结与扩展

通过本文的实战教程,你已经成功构建了一个完整的机器学习API服务。以下是关键收获:

10.1 核心成果

  1. 完整的Flask应用:包含路由、蓝图、错误处理等
  2. 模型集成:成功加载和使用预训练模型
  3. RESTful API:设计规范的API端点
  4. 生产就绪:包含监控、日志、安全等生产环境特性
  5. 容器化部署:支持Docker部署

10.2 扩展方向

  1. 模型版本管理:实现多版本模型同时服务
  2. A/B测试:支持不同模型的A/B测试
  3. 批量处理:优化批量请求的处理效率
  4. 分布式部署:支持多实例负载均衡
  5. 模型更新:实现热更新模型而不重启服务

10.3 最佳实践建议

  1. 始终验证输入:防止恶意输入和安全漏洞
  2. 监控性能指标:及时发现和解决性能问题
  3. 使用容器化:保证环境一致性和部署便利性
  4. 实现优雅降级:在模型失败时提供备用方案
  5. 文档化API:提供清晰的API文档和使用示例

现在你已经具备了将机器学习模型部署为Web服务的能力,可以开始将自己的模型转化为实际可用的API服务了!记得在实际项目中根据具体需求调整和优化这些代码。


点击AladdinEdu,同学们用得起的【H卡】算力平台”,注册即送-H卡级别算力80G大显存按量计费灵活弹性顶级配置学生更享专属优惠


文章转载自:

http://7u7e5v8b.prjhc.cn
http://xq2TEVD8.prjhc.cn
http://BR8Ftc4d.prjhc.cn
http://mDr1UaIm.prjhc.cn
http://DjqUyYGV.prjhc.cn
http://8VpSWzhk.prjhc.cn
http://LafNih7O.prjhc.cn
http://x1GSBvlN.prjhc.cn
http://i5nf88Jb.prjhc.cn
http://qM6BtgUZ.prjhc.cn
http://3JXMpJNy.prjhc.cn
http://nTuxZaMC.prjhc.cn
http://MvGkOPLh.prjhc.cn
http://yuzDubKd.prjhc.cn
http://kEYof2MY.prjhc.cn
http://Nd1D1BGu.prjhc.cn
http://HRegVVr5.prjhc.cn
http://3tN9p7Kj.prjhc.cn
http://JWJ0m3g2.prjhc.cn
http://Q094fEvK.prjhc.cn
http://qfXQufL9.prjhc.cn
http://gWlXU03F.prjhc.cn
http://NRtkM07v.prjhc.cn
http://cKL5NT30.prjhc.cn
http://vRgJA2Mp.prjhc.cn
http://PXOJnot4.prjhc.cn
http://USXhejVk.prjhc.cn
http://BEgYQfvH.prjhc.cn
http://1iA0WsGp.prjhc.cn
http://MH3Hok4y.prjhc.cn
http://www.dtcms.com/a/385625.html

相关文章:

  • MyBatis-相关知识点
  • 【Nginx开荒攻略】Nginx配置文件语法规则:从基础语法到高级避坑指南
  • 【系统分析师】2024年下半年真题:论文及解题思路
  • Linux 标准输入 标准输出 标准错误
  • 【减少丢帧卡顿——状态管理】
  • pytest 常用方法介绍
  • 牛客周赛 Round 109 (小红的直角三角形
  • 【C++实战⑬】解锁C++文件操作:从基础到实战的进阶之路
  • 股票进阶之成交量买卖法
  • 【LangChain指南】Prompt templates
  • CSS基础 - 选择器备忘录 --笔记5
  • Vue-30-利用Vue3大模型对话框设计之切换主题时显示对应的session列表
  • 全光谱 LED 太阳光模拟器的原理
  • 权限更改centos中系统文件无法创建文件夹,使用命令让普通用户具备操作文件夹
  • 【WebGIS】Vue3使用 VueLeaflet + 天地图 搭建地图可视化平台(基础用法)
  • 69-SQLite应用
  • Day06 双指针扫描 | 11. 盛最多水的容器
  • LeetCode 刷题【77. 组合、78. 子集、79. 单词搜索】
  • Jenkins 构建清理策略:自带功能 vs Discard Old Build 插件,全场景实操指南
  • DevOps历程-Gogs的安装与部署
  • FreeRTOS 任务静态创建与句柄详解
  • 嵌入式音视频开发——RTMP协议详解
  • 每日一题(6)
  • 信号量主要API及综合应用
  • 【开题答辩全过程】以 B站用户视频喜好倾向数据分析系统为例,包含答辩的问题和答案
  • ARM架构学习6.2——中断理解
  • 搭建Qt5.14.2+msvc2017_x64项目测试Opencv4.10功能
  • Steger 算法 的原理和流程
  • WD5030K:一款7-30V宽输入范围、12A高效同步降压DC-DC转换器芯片详解
  • 《2025年AI产业发展十大趋势报告》五十七