Pytorhc Lightning进阶:一篇实例玩转Pytorhc Lightning 让训练更高效

Pytorhc Lightning进阶:一篇实例玩转Pytorhc Lightning 让训练更高效

Pytorhc Lightning 主要包含以下几大类,主要围绕以下讲解:

  • 模型,PyTorch Lightning 的核心是继承 pl.LightningModule
  • 数据,数据模块继承pl.LightningDataModule
  • 回调函数的构造和使用,以及自定义
  • 钩子函数使用,模型中、数据类中、回调函数中调用先后
  • 日志记录,一般用Pytorhc Lightning自带的tensorboard

1. 定义模型类

1.1 如下模型的基本结构

import torch  
from torch import nn  
from torch.utils.data import DataLoader, random_split  
from torchvision.datasets import MNIST  
from torchvision.transforms import ToTensor  
import pytorch_lightning as pl  class LitModel(pl.LightningModule):  def __init__(self, imgsize=28, hidden=128, num_class=10):  super().__init__()  self.flatten = nn.Flatten()  self.net = nn.Sequential(  nn.Linear(imgsize * imgsize, hidden),  nn.ReLU(),  nn.Linear(hidden, num_class)  )  self.loss_fn = nn.CrossEntropyLoss()  def forward(self, x):  x = self.flatten(x)  return self.net(x)  def training_step(self, batch, batch_idx):  x, y = batch  pred = self(x)  loss = self.loss_fn(pred, y)  self.log("train_loss", loss)  # 自动记录训练损失  return loss  def validation_step(self, batch, batch_idx):  x, y = batch  pred = self(x)  loss = self.loss_fn(pred, y)  self.log("val_loss", loss)  # 自动记录验证损失  return loss  def test_step(self, batch, batch_idx):  x, y = batch  pred = self(x)  loss = self.loss_fn(pred, y)  self.log("test_loss", loss)  # 自动记录测试损失  return loss  def configure_optimizers(self):  return torch.optim.Adam(self.parameters(), lr=1e-3)

1.2 方法validation_step training_step test_step

传入两个必须参数是batch,batch_idx,名字不能改。当然还有其他的,但是基本不怎么用。

  • batch 就是从dataloader 中返回的结果
  • batch_idx 记录当前epoch数据批次的索引,比如我想每隔100step去记录日志,可以if batch_idx % 100 == 0:
  • training_step 需要返回进行反向传播的loss,其他两个可以不用,多个优化器需要手动实现反向传播(高版本)
  • validation_step training_step 训练时一定要有; test_step是模型测试的时候调用,训练时可以不要

1.3 优化器和调度定义方法configure_optimizers

该方法不需要额外传参,如果有多个优化器则按照return [optimizer1, optimizer1...],[scheduler1, scheduler2,...] 当然还可以按照字典形式返回,这里拿以列表返回举例。


1.4 多个优化器手动实现反向传播

我们改写一下1.1 的模型来看看怎么实现多个优化器情况。这里假设有多个优化器

class LitModel(pl.LightningModule):  def __init__(self, imgsize=28, hidden=128, num_class=10):  super().__init__()  self.flatten = nn.Flatten()  ####################  # 1. 修改模型为两部分方便应用不同的优化器  self.feature_extractor = nn.Sequential(  nn.Linear(imgsize * imgsize, hidden),  nn.ReLU()  )  self.classifier = nn.Sequential(  nn.Linear(hidden, num_class)  )  #关闭自动优化  self.automatic_optimization = False  ####################  self.loss_fn = nn.CrossEntropyLoss()  # 增加保存参数  self.save_hyperparameters()  def forward(self, x):  x = self.flatten(x)  features = self.feature_extractor(x)  return self.classifier(features)  def training_step(self, batch, batch_idx):  x, y = batch  pred = self(x)  loss = self.loss_fn(pred, y)  opt1, opt2 = self.optimizers()  #3,手动优化  if batch_idx % 2 != 0:  opt1.zero_grad()  self.manual_backward(loss)  opt1.step()  # 每2步更新一次分类器  if batch_idx % 2 == 0:  opt2.zero_grad()  self.manual_backward(loss)  opt2.step()  self.log("train_loss", loss)  # 自动记录训练损失  # 不在返回loss,因为已经手动实现  return None  def validation_step(self, batch, batch_idx):  x, y = batch  pred = self(x)  loss = self.loss_fn(pred, y)  self.log("val_loss", loss)  # 自动记录验证损失  return loss  def test_step(self, batch, batch_idx):  x, y = batch  pred = self(x)  loss = self.loss_fn(pred, y)  self.log("test_loss", loss)  # 自动记录测试损失  return loss  def configure_optimizers(self):  #2. 这里对不同参数应用不同优化和调度,  optimizer1 = torch.optim.Adam(  self.feature_extractor.parameters(),  lr=1e-3,  weight_decay=1e-4  )  optimizer2 = torch.optim.SGD(  self.classifier.parameters(),  lr=1e-2,  momentum=0.9  )  # 假如这里只需对第二个优化器进行学习率调度  scheduler2 = torch.optim.lr_scheduler.MultiStepLR(  optimizer2,  milestones=[1,3,5],  gamma=0.5,  last_epoch=-1)  # 返回值return [optimizer1, optimizer2], scheduler2  # 4. 增加钩子函数,每个batch后手动更新学习率  def on_train_batch_end(self, outputs, batch, batch_idx):  # 获取调度器  scheduler2 = self.lr_schedulers()  # 更新调度器(按批次)  scheduler2.step()  # 记录学习率  lr = scheduler2.get_last_lr()[0]  self.log("lr", lr, prog_bar=True)  # 5. 增加钩子函数,模型中保存参数配置  def on_save_checkpoint(self, checkpoint: dict) -> None:  checkpoint["save_data"] = self.hparams

2 数据

class MNISTDataModule(pl.LightningDataModule):  def __init__(self, batch_size=32):  super().__init__()  self.batch_size = batch_size  # 数据处理部分,只会加载一次  def prepare_data(self):  # 下载数据集  MNIST(root="data", train=True, download=True)  MNIST(root="data", train=False, download=True)  # 在分布式训练的时候,每个进程都会加载  def setup(self, stage=None):  # 数据预处理和划分  transform = ToTensor()  mnist_full = MNIST(root="data", train=True, transform=transform)  self.mnist_train, self.mnist_val = random_split(mnist_full, [55000, 5000])  self.mnist_test = MNIST(root="data", train=False, transform=transform)  def train_dataloader(self):  return DataLoader(self.mnist_train, batch_size=self.batch_size, shuffle=True)  def val_dataloader(self):  return DataLoader(self.mnist_val, batch_size=self.batch_size)  def test_dataloader(self):  # 当然如果没有test,只需要返回None  return DataLoader(self.mnist_test, batch_size=self.batch_size)

以上是数据模块的基本结构

3 回调函数定义

下面是一个定义的例子,作用是保存config

import torch  
from torch import nn  
from torch.utils.data import DataLoader, random_split  
from torchvision.datasets import MNIST  
from torchvision.transforms import ToTensor  
import pytorch_lightning as pl  
from typing import Dict, Any  
import os  
from pytorch_lightning.callbacks import ModelCheckpoint, Callback, LearningRateMonitor  
from omegaconf import OmegaConfclass SetupCallback(Callback):  def __init__(self, now, cfgdir, config):  super().__init__()  self.now = now  self.cfgdir = cfgdir  self.config = config  def on_train_start(self, trainer, pl_module):  # 只在主进程保留  if trainer.global_rank == 0:  print("Project config")  OmegaConf.save(self.config,  os.path.join(self.cfgdir, "{}-project.yaml".format(self.now)))  # 把config 也存到模型中  def on_save_checkpoint(  self, trainer: "pl.Trainer", pl_module: "pl.LightningModule", checkpoint: Dict[str, Any]  ) -> None:  checkpoint["cfg_project"] = self.config

这里on_train_start``on_save_checkpoint 就是定义在回调函数中的钩子函数,下面介绍这几个钩子函数使用方法

4 几个常见的钩子函数使用

以下是三个钩子函数在模型类和回调函数中使用方法及调用顺序的简明对比表格:

钩子函数位置方法签名典型用途调用顺序执行频率
on_fit_start模型类def on_fit_start(self)全局初始化、分布式设置先执行整个训练过程一次
回调函数def on_fit_start(self, trainer, pl_module)准备日志系统、设置全局状态后执行整个训练过程一次
on_train_start模型类def on_train_start(self)初始化训练指标、计时器先执行每个训练循环一次
回调函数def on_train_start(self, trainer, pl_module)重置回调状态、训练前准备后执行每个训练循环一次
on_save_checkpoint模型类def on_save_checkpoint(self, checkpoint)保存模型额外状态后执行每次保存检查点时
回调函数def on_save_checkpoint(self, trainer, pl_module, checkpoint)保存回调状态、添加元数据先执行每次保存检查点时

调用顺序示意图

训练开始
├── on_fit_start
│   ├── 模型类.on_fit_start (1)
│   └── 回调函数.on_fit_start (2)
│
├── on_train_start
│   ├── 模型类.on_train_start (3)
│   └── 回调函数.on_train_start (4)
│
├── 训练周期...
│
└── 保存检查点├── 回调函数.on_save_checkpoint (5)└── 模型类.on_save_checkpoint (6)

5 完整代码

simple.yaml

trainer:accelerator: gpudevices: [1]max_epochs: 100
call_back:modelckpt:filename: "{epoch:03}-{train_loss:.2f}-{val_loss:.2f}"save_top_k: -1save_step: trueevery_n_epochs: 10verbose: truesave_last: true
model:target: main_pl.LitModelparams:imgsize: 28hidden: 128num_class: 10
data:target: main_pl.MNISTDataModuleparams:batch_size: 32

main_pl.py

import torch  
from torch import nn  
from torch.utils.data import DataLoader, random_split  
from torchvision.datasets import MNIST  
from torchvision.transforms import ToTensor  
import pytorch_lightning as pl  
from typing import Dict, Any  
from pytorch_lightning.callbacks import ModelCheckpoint, Callback, LearningRateMonitor  
from omegaconf import OmegaConf  
import argparse, os, datetime, importlib, glob  
from pytorch_lightning import Trainer  def get_obj_from_str(string, reload=False):  module, cls = string.rsplit(".", 1)  if reload:  module_imp = importlib.import_module(module)  importlib.reload(module_imp)  return getattr(importlib.import_module(module, package=None), cls)  def instantiate_from_config(config):  if not "target" in config:  raise KeyError("Expected key `target` to instantiate.")  return get_obj_from_str(config["target"])(**config.get("params", dict()))  # 1. 定义 LightningModule 子类  
class LitModel(pl.LightningModule):  def __init__(self, imgsize=28, hidden=128, num_class=10):  super().__init__()  self.flatten = nn.Flatten()  ####################  # 1. 修改模型为两部分方便应用不同的优化器  self.feature_extractor = nn.Sequential(  nn.Linear(imgsize * imgsize, hidden),  nn.ReLU()  )  self.classifier = nn.Sequential(  nn.Linear(hidden, num_class)  )  #关闭自动优化  self.automatic_optimization = False  ####################  self.loss_fn = nn.CrossEntropyLoss()  # 增加保存参数  self.save_hyperparameters()  def forward(self, x):  x = self.flatten(x)  features = self.feature_extractor(x)  return self.classifier(features)  def training_step(self, batch, batch_idx):  x, y = batch  pred = self(x)  loss = self.loss_fn(pred, y)  opt1, opt2 = self.optimizers()  #3,手动优化  if batch_idx % 2 != 0:  opt1.zero_grad()  self.manual_backward(loss)  opt1.step()  # 每2步更新一次分类器  if batch_idx % 2 == 0:  opt2.zero_grad()  self.manual_backward(loss)  opt2.step()  self.log("train_loss", loss)  # 自动记录训练损失  # 不在返回loss,因为已经手动实现  return None  def validation_step(self, batch, batch_idx):  x, y = batch  pred = self(x)  loss = self.loss_fn(pred, y)  self.log("val_loss", loss)  # 自动记录验证损失  return loss  def test_step(self, batch, batch_idx):  x, y = batch  pred = self(x)  loss = self.loss_fn(pred, y)  self.log("test_loss", loss)  # 自动记录测试损失  return loss  def configure_optimizers(self):  #2. 这里对不同参数应用不同优化和调度,  optimizer1 = torch.optim.Adam(  self.feature_extractor.parameters(),  lr=1e-3,  weight_decay=1e-4  )  optimizer2 = torch.optim.SGD(  self.classifier.parameters(),  lr=1e-2,  momentum=0.9  )  # 假如这里只需对第二个优化器进行学习率调度  scheduler2 = torch.optim.lr_scheduler.MultiStepLR(  optimizer2,  milestones=[1,3,5],  gamma=0.5,  last_epoch=-1)  # 返回值return [optimizer1, optimizer2], scheduler2  # 4. 增加钩子函数,每个batch后手动更新学习率  def on_train_batch_end(self, outputs, batch, batch_idx):  # 获取调度器  scheduler2 = self.lr_schedulers()  # 更新调度器(按批次)  scheduler2.step()  # 记录学习率  lr = scheduler2.get_last_lr()[0]  self.log("lr", lr, prog_bar=True)  def on_save_checkpoint(self, checkpoint: dict) -> None:  checkpoint["save_data"] = self.hparams  # 2. 准备数据模块  
class MNISTDataModule(pl.LightningDataModule):  def __init__(self, batch_size=32):  super().__init__()  self.batch_size = batch_size  # 数据处理部分,只会加载一次  def prepare_data(self):  # 下载数据集  MNIST(root="data", train=True, download=True)  MNIST(root="data", train=False, download=True)  # 在分布式训练的时候,每个进程都会加载  def setup(self, stage=None):  # 数据预处理和划分  transform = ToTensor()  mnist_full = MNIST(root="data", train=True, transform=transform)  self.mnist_train, self.mnist_val = random_split(mnist_full, [55000, 5000])  self.mnist_test = MNIST(root="data", train=False, transform=transform)  def train_dataloader(self):  return DataLoader(self.mnist_train, batch_size=self.batch_size, shuffle=True)  def val_dataloader(self):  return DataLoader(self.mnist_val, batch_size=self.batch_size)  def test_dataloader(self):  # 当然如果没有test,只需要返回None  return DataLoader(self.mnist_test, batch_size=self.batch_size)  class SetupCallback(Callback):  def __init__(self, now, cfgdir, config):  super().__init__()  self.now = now  self.cfgdir = cfgdir  self.config = config  def on_train_start(self, trainer, pl_module):  # 只在主进程保留  if trainer.global_rank == 0:  print("Project config")  OmegaConf.save(self.config,  os.path.join(self.cfgdir, "{}-project.yaml".format(self.now)))  # 把config 也存到模型中  def on_save_checkpoint(  self, trainer: "pl.Trainer", pl_module: "pl.LightningModule", checkpoint: Dict[str, Any]  ) -> None:  checkpoint["cfg_project"] = self.config  if __name__ == '__main__':  now = datetime.datetime.now().strftime("%Y-%m-%dT%H-%M-%S")  configs_path = 'simple.yaml'  logdir = os.path.join("logs", now)  config = OmegaConf.load(configs_path)  #0. 从config中读取 trainer 基本参数  accelerator: gpu devices: [0] max_epochs: 100    trainer_kwargs = dict(config.trainer)  #1, 定义loss loger trainer_kwargs  logger_cfg =  {  "target": "pytorch_lightning.loggers.TensorBoardLogger",  "params": {  "name": "tensorboard",  "save_dir": logdir,  }  }  logger_cfg = OmegaConf.create(logger_cfg)  trainer_kwargs["logger"] = instantiate_from_config(logger_cfg)  #2. callback  modelckpt_params =  config.call_back.modelckpt  callbacks_cfg = {  "setup_callback": {  "target": "main_pl.SetupCallback",  "params": {  "now": now,  "cfgdir": logdir,  "config": config,  }  },  "learning_rate_logger": {  "target": "main_pl.LearningRateMonitor",  "params": {  "logging_interval": "step",  }  },  "checkpoint_callback":{  "target": "pytorch_lightning.callbacks.ModelCheckpoint",  "params": {  "dirpath": logdir,  "filename": modelckpt_params.filename,  "save_top_k": modelckpt_params.save_top_k,  "verbose": modelckpt_params.verbose,  "save_last": modelckpt_params.save_last,  "every_n_epochs": modelckpt_params.every_n_epochs,  }  }  }  callbacks = [instantiate_from_config(callbacks_cfg[k]) for k in callbacks_cfg]  trainer_kwargs["callbacks"] = callbacks  print(trainer_kwargs)  #3. 构建trainer  trainer = Trainer(**trainer_kwargs)  #4. 构建data 和 model    # build data and model    data = instantiate_from_config(config.data)  model = instantiate_from_config(config.model)  #5. 训练  try:  trainer.fit(model, data)  except Exception:  raise

运行结果:
logs中会保存我们的配置和tensorboard的日志以及模型
在这里插入图片描述

5 总结

PyTorch Lightning 是一个轻量级的 PyTorch 封装库,它通过结构化代码和自动化工程细节,显著提升深度学习研究和开发的效率。以下是其主要优势总结:


1. 代码结构化与可读性

  • 关注科研而非工程:将模型定义、训练逻辑、工程代码解耦
  • 标准化接口:强制使用 LightningModule 方法(training_step, configure_optimizers 等)
  • 减少样板代码:训练循环代码量减少 80%+
# 传统 PyTorch vs Lightning
# ---------------------------
# PyTorch: 需手动编写训练循环
for epoch in epochs:for batch in data:optimizer.zero_grad()loss = model(batch)loss.backward()optimizer.step()# Lightning: 只需定义逻辑
class LitModel(pl.LightningModule):def training_step(self, batch, batch_idx):x, y = batchy_hat = self(x)loss = F.cross_entropy(y_hat, y)return loss

2. 自动化工程细节

功能实现方式优势
分布式训练Trainer(accelerator="gpu", devices=4)单行代码启用多GPU/TPU
混合精度训练Trainer(precision="16-mixed")显存节省+速度提升
梯度累积Trainer(accumulate_grad_batches=4)模拟更大batch_size
早停机制callbacks=[EarlyStopping(...)]自动防止过拟合

3. 可复现性与实验管理

  • 版本控制:自动保存超参数 (self.save_hyperparameters())
  • 实验跟踪:内置支持 TensorBoard/W&B/MLFlow
  • 完整检查点:一键保存模型+优化器+超参数状态
# 自动记录所有实验
trainer = Trainer(logger=TensorBoardLogger("logs/"))

4. 硬件无关性

单行切换硬件环境:

# CPU → GPU → TPU → 多节点分布式
trainer = Trainer(accelerator="auto",      # 自动检测硬件devices="auto",          # 使用所有可用设备strategy="ddp_find_unused_parameters_true"  # 分布式策略
)

5. 调试与开发效率

# 快速验证代码
trainer = Trainer(fast_dev_run=True)  # 只跑1个batch# 性能分析
trainer = Trainer(profiler="advanced")  # 识别瓶颈

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/87146.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/87146.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大模型算法面试笔记——注意力Transformer流程/面试题篇

学习资料来源于字母站大学 1 Transformer架构 基于编码器-解码器的架构来处理序列对。跟使用注意力的seq2seq不同,Transformer是基于纯注意力。 2 注意力 2.1 自注意力机制 使用注意力:需要根据整个序列进行预测,对于同一input&#xf…

Rust 定义与实例化结构体

文章目录 Rust 定义与实例化结构体5.1 结构体的定义与意义5.2 结构体实例化5.2.1 基本实例化5.2.2 可变性规则5.2.3 字段初始化简写5.2.4 结构体更新语法 5.3 特殊结构体类型5.3.1 元组结构体(Tuple Struct)5.3.2 类单元结构体(Unit-Like Str…

ELK日志分析系统(filebeat+logstash+elasticsearch+kibana)

一、ELK 平台介绍 1、ELK 概述 日志主要包括系统日志、应用程序日志和安全日志。系统运维和开发人员可以通过日志了解服务器软硬件信息、检查配置过程中的错误及错误发生的原因。经常分析日志可以了解服务器的负荷,性能安全性,从而及时采取措施纠正错误。…

JS基础4—jQuery

jQuery常用内容 jQuery 介绍jQuery 获取方式基本选择器 (最常用)层级选择器 (基于元素间关系)过滤选择器 (基于特定条件) jQuery事件绑定jQuery 方法调用jQuery遍历jQuery 获取与设置jQuery 添加与删除jQuery CSS 类jQuery - AJAX 总结 jQuery 介绍 jQuery 是一个轻量级、快速…

时钟周期是什么?

时钟周期(Clock Cycle)是什么? 时钟周期(Clock Cycle)是计算机系统中一个最基础的时间单位,也称为时钟节拍或时钟周期时间(Clock Period)。它由系统时钟发生器产生的一个周期性脉冲…

如何用SEO优化长尾关键词?

内容概要 在SEO优化领域,长尾关键词扮演着至关重要的角色,它们能有效提升网站在搜索引擎中的可见度和流量转化率。本文将全面解析如何通过系统方法优化长尾关键词,涵盖从基础理论到实战应用的完整流程。核心内容包括利用专业工具进行关键词挖…

电子面单系统开发全解析

一、如果要做电子面单系统,怎么做? 开发电子面单系统是一项复杂且涉及多方面考量的工程,涵盖需求分析、系统架构设计、技术选型、接口对接、安全性保障、第三方服务选择以及部署与维护等关键环节。 电子面单系统开发步骤 需求分析&#xf…

UE5 - 制作《塞尔达传说》中林克的技能 - 18 - 磁力抓取器

让我们继续《塞尔达传说》中林克技能的制作!!! UE版本:5.6.0 VS版本:2022 本章节的核心目标:磁力抓取器 先让我们看一下完成后的效果: 18_磁力抓取器 大纲如下: 引言功能架构与核心逻辑物理材质与场景配置代码实现:从识别到操控操作说明1.引言 在《塞尔达传说》中,林…

基于ApachePOI实现百度POI分类快速导入PostgreSQL数据库实战

目录 前言 一、百度POI分类简介 1、数据表格 2、分类结构 二、从Excel导入到PG数据库 1、Excel解析流程 2、数据入库 3、入库成果及检索 三、总结 前言 在上一篇博文中,我们对高德POI分类进行了深入剖析 并对Excel 中 POI 分类数据的存储结构特点进行了详细介…

学习经验分享【41】YOLOv13:基于超图增强自适应视觉感知的实时目标检测

YOLO算法更新速度很快,已经出到V13版本,后续大家有想发论文或者搞项目可更新自己的baseline了。 摘要:YOLO 系列模型凭借其卓越的精度和计算效率,在实时目标检测领域占据主导地位。然而,YOLOv11 及早期版本的卷积架构&…

Handling outliers in non-blind image deconvolution论文阅读

Handling outliers in non-blind image deconvolution 1. 研究目标与实际意义2. 创新方法:基于EM的异常值建模2.1 新模糊模型2.1.1 目标函数2.2 EM框架:迭代优化二元掩码2.2.1 E步:计算后验权重 E [ m x ] E[m_x] E[mx​]2.2.2 M步:加权正则化反卷积2.3 优化加速技术2.3.1…

Redis 功能扩展:Lua 脚本对 Redis 的扩展

Redis 是一个高性能的内存数据库,支持多种数据结构,如字符串、哈希、列表、集合和有序集合。为了增强其功能,Redis 引入了 Lua 脚本支持,使开发者可以编写自定义的脚本,确保操作的原子性并提高复杂操作的性能。本文将详…

七天学完十大机器学习经典算法-06.支持向量机(SVM):分类边界的艺术——深入浅出指南

接上一篇《七天学完十大机器学习经典算法-05.从投票到分类:K近邻(KNN)算法完全指南》 想象你要在操场上为两个班级划活动区域,如何画出一条最公平的分界线?这条线不仅要分开两班学生,还要让两个班都离分界线尽可能远——这就是支持…

python如何安装PyQt6-stubs依赖包

PyQt6-stubs 是为 PyQt6 提供类型提示(Type Hints)和 IDE 智能补全支持的第三方补丁包,特别适用于 PyCharm、VS Code 等现代 IDE。它对开发者在编码时帮助极大。 一、安装方法 需要提前安装好git,然后克隆PyQt6-stubs源码&#xf…

创宇智脑 MCP 赋能 AiPy,IP 风险调查效率实现 10 倍飞跃,威胁分析一键生成

还记得上个月那个焦头烂额的凌晨三点吗?监控大屏突然疯狂闪烁,500 多个 IP 地址同时出现异常访问,密密麻麻的数据流在屏幕上跳动,像极了一张让人窒息的大网。我和团队成员瞪着布满血丝的眼睛,手动排查每一个 IP&#x…

使用SRS+ffmpeg实现https推流flv

1修改SRS的live.conf配置如下: # Live streaming config for SRS. # see full.conf for detail config.listen 1935; max_connections 1000; srs_log_tank console; daemon off;http_api {enabled on;listen …

力扣网编程题:合并两个有序数组(双指针解法)

一. 简介 上一篇文章对"合并两个有序数组"题目,使用了暴力解法,算法时间复杂度比较高。文章如下: 力扣网编程题:合并两个有序数组(直接解法)-CSDN博客 本文满足进阶要求,算法时间复…

数据结构之 【树的简介】(树的(相关)概念、二叉树的概念、部分性质、满二叉树、完全二叉树)

目录 1.树的概念及结构 1.1树的概念 1.2树的相关概念 1.3树的表示 1.4树在实际中的应用 2.二叉树概念及结构 2.1二叉树的概念 2.2特殊的二叉树 2.3二叉树的性质 2.4应用题 1.树的概念及结构 1.1树的概念 树是一种非线性的数据结构,由 n(n…

Redis-7.4.3-Windows-x64下载安装使用

Redis软件包下载地址链接:https://github.com/redis-windows/redis-windows/releases 检查或者修改配置文件redis.conf: #如果允许外部其他主机访问本机redis,设置成:bind 0.0.0.0 bind 127.0.0.1 protected-mode yes #设置端口…

Educational Codeforces Round 180 (Rated for Div. 2)

AB 略 C 对于axayaz>max(2*az,an),枚举y z 二分x D 首先,长度为1的边的已经有n-1条,那么构造的图中只能存在一条长度为2的好边。我们先构造出一个图只存在n-1条好边,我们发现对于一个点所有连接它的边要不均指向它要不均背…