第16周:LSTM-火灾温度预测

目录

前言

一、LSTM简介

1.1 LSTM的本质

1.2 LSTM的提出

1.3 LSTM的原理

1.3.1 RNN原理介绍

1.3.2 LSTM原理介绍

二、前期准备

2.1 导入库、设置GPU

2.2 导入数据

2.3 构建数据集 

2.3.1 数据集预处理

2.3.2 设置X,y

2.3.3 缺失值检测

2.3.4 划分数据集

三、模型训练

3.1 构建模型

3.1.1 nn.LSTM()函数详解

3.1.2 模型搭建

3.2 训练函数

3.3 测试函数

3.4 正式训练

四、模型评估

4.1 Loss图

4.2 调用模型进行预测

4.3 R^2值评估

五、拔高尝试

总结


前言

  • 🍨 本文为[🔗365天深度学习训练营](https://mp.weixin.qq.com/s/0dvHCaOoFnW8SCp3JpzKxg) 中的学习记录博客
  • 🍖 原作者:[K同学啊](https://mtyjkh.blog.csdn.net/)

说在前面

本周目标:对时序数据进行预测,了解LSTM是什么,并使用其构建一个完整的程序,并使最后的决定系数R^2达到0.83;拔高目标——使用第1~8个时刻的数据预测第9~10个时刻的温度数据

我的环境:Python3.8、Pycharm2020、torch1.12.1+cu113

数据来源:[K同学啊](https://mtyjkh.blog.csdn.net/)


一、LSTM简介

LSTM全称为长短期记忆网络(Long Short Term Memeory networks),是一种特殊的RNN,能够

析任务而创建的。LSTM由Hochreiter & Schmidhuber(1997)提出,许多研究者进行一系列的工作对其改进并使之发扬光大。LSTM在许多问题上效果非常好,现在被广泛使用。

1.1 LSTM的本质

RNN面临的问题:RNN在处理长序列时面临的主要问题——短时记忆和梯度消失/爆炸

1)短时记忆:

问题:RNN在处理长序列时,由于信息的传递是通过隐藏状态进行的,随着时间的推移,较早时间步的信息可能会在传递到后面的时间步时逐渐消失或被覆盖。

影响:导致RNN难以捕捉和利用序列中的长期依赖关系,从而限制其在处理复杂任务时的性能

2)梯度消失/梯度爆炸

问题:在RNN的反向传播过程中,梯度会随着时间步的推移而逐渐消失(变得非常小)或爆炸(变得非常大)

影响:梯度消失使得RNN在训练时难以学习到长期依赖关系,因为较早时间步的梯度信息在反向传播到初始层时几乎为零。梯度爆炸则可能导致训练过程不稳定,权重更新过大,甚至导致数值溢出。

1.2 LSTM的提出

 LSTM是一种RNN的辩题,解决了传统RNN在处理长序列数据时的问题,特别是在面对长时间滞后关系时容易出现的梯度消失或梯度爆炸问题。

LSTM通过引入“门”的结构,包括输入门、遗忘门和输出门,以及细胞状态,解决了一下问题:

1)长期依赖问题:在传统的RNN中,当序列非常长时,网络往往会遇到梯度消失或梯度爆炸的问题,导致难以捕捉到序列中长距离的依赖关系。LSTM通过细胞状态来传递信息,避免了梯度消失或爆炸,从而更好地捕捉长期依赖关系;

2)遗忘和记忆:LSTM的门控结构允许网络选择性地忘记或记住某些信息,这使得网络可以更好地处理序列中的噪声或不重要的信息,同时保留重要的长期依赖关系;

3)梯度传播:LSTM通过门控结构,似的梯度可以在时间更好地传播,从而使得网络的训练更加稳定和高效。

所以LSTM通过引入门控结构和细胞状态,有效地解决了传统RNN在处理长序列数据时遇到的梯度消失、长期依赖等问题,使得其在语言建模、时间序列预测、机器翻译等任务中取得显著的进展。

1.3 LSTM的原理

1.3.1 RNN原理介绍

1)隐藏状态的传递

过程描述:在处理序列数据时,RNN将前一时间步的隐藏状态传递给下一个时间步

作用:隐藏状态充当了神经网络的“记忆”,它包含了网络之前所见过的数据的相关信息

重要性:这种传递机制使得RNN能够捕捉序列中的时序依赖关系

   将隐藏状态传递给下一个时间步

2)隐藏状态的计算

细胞结构:RNN的一个细胞接收当前时间步的输入和前一时间步的隐藏状态

组合方式:当前输入和先前隐藏状态被组合成一个向量,这个向量融合了当前和先前的信息

激活函数:组合后的向量经过一个tanh激活函数的处理,输出新的隐藏状态。这个新的隐藏状态既包含了当前输入的信息,也包含了之前所有输入的历史信息。

tanh激活函数(区间-1~1)

输出:新的隐藏状态被输出,并传递给下一个时间步,继续参与序列的处理过程

RNN的细胞结构和运算

1.3.2 LSTM原理介绍

原始RNN的隐藏层只有一个状态,即h,它对于短期的输入非常敏感。那么如果我们再增加一个门(gate)机制用于控制特征的流通和损失,即c,让它来保存长期的状态,这就是长短时记忆网络(Long Short Term Memory,LSTM)

新增加的状态c称为单元状态,把LSTM按照时间维度展开:

在t时刻,LSTM的输入有三个:当前时刻网络的输出值x_{t}、上一时刻LSTM的输出值h_{t-1}、以及上一时刻的记忆单元向量c_{t-1};LSTM的输出有两个:当前时刻的隐藏状态向量h_{t}、和当前时刻的记忆单元状态向量c_{t}

1)遗忘门

f_{t}叫做遗忘门,表示C_{t-1}的哪些特征被用于计算C_{t}f_{t}是一个向量,向量的每个元素均位于(0~1)范围内。通常我们使用sigmoid作为激活函数,sigmoid的输出是一个介于(0~1)区间内的值,但是当观察一个训练好的LSTM时,会发现门的值绝大多数都非常接近0或者1,其余的值少之又少。

作用:决定哪些旧信息应该从记忆单元中遗忘或移除

组成:遗忘门仅由一个sigmoid激活函数组成

sigmoid激活函数(区间0~1)

2)输入门

作用:决定哪些新信息应该被添加到记忆单元中

组成:输入门由一个sigmoid激活函数和一个tanh激活函数组成。sigmoid函数决定哪些信息是重要的,而tanh函数则生成新的候选信息

运算:输入门的输出与候选信息相乘,得到的结果将在记忆单元更新时被考虑

输入门(sigmoid激活函数+tanh激活函数)

3)输出门

作用:决定记忆单元中的哪些信息应该被输出到当前时间步的隐藏状态h_{t}

组成:输出门同样由一个sigmoid激活函数和一个tanh激活函数组成。sigmoid函数决定哪些信息应该被输出,而tanh函数则处理记忆单元的状态以准备输出

运算:sigmoid函数的输出与经过tanh函数处理的记忆单元状态相乘,得到的结果即为当前时间步的隐藏状态h_{t}

输出门(sigmoid激活函数+tanh激活函数)

二、前期准备

2.1 导入库、设置GPU

代码如下:

import torch.nn.functional as F
import numpy as np
import pandas as pd
import torch
from torch import nn
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.preprocessing import MinMaxScaler
from torch.utils.data import TensorDataset, DataLoader
from sklearn import metrics
import warnings
warnings.filterwarnings("ignore")device = "cuda" if torch.cuda.is_available() else "cpu"
print("Using {} device".format(device))

打印输出:Using cuda device

2.2 导入数据

代码如下:

data = pd.read_csv("woodpine2.csv")
#data.to(device)
print(data.head())#数据集可视化
plt.rcParams['savefig.dpi'] = 500  #图片像素
plt.rcParams['figure.dpi'] = 500   #分辨率fig, ax = plt.subplots(1, 3, constrained_layout=True, figsize=(14, 3))sns.lineplot(data=data["Tem1"], ax=ax[0])
sns.lineplot(data=data["CO 1"], ax=ax[1])
sns.lineplot(data=data["Soot 1"], ax=ax[2])
plt.show()dataFrame = data.iloc[:, 1:]
print(dataFrame)

打印输出:

Time  Tem1  CO 1  Soot 1
0  0.000  25.0   0.0     0.0
1  0.228  25.0   0.0     0.0
2  0.456  25.0   0.0     0.0
3  0.685  25.0   0.0     0.0
4  0.913  25.0   0.0     0.0

       Tem1      CO 1    Soot 1
0      25.0  0.000000  0.000000
1      25.0  0.000000  0.000000
2      25.0  0.000000  0.000000
3      25.0  0.000000  0.000000
4      25.0  0.000000  0.000000
...     ...       ...       ...
5943  295.0  0.000077  0.000496
5944  294.0  0.000077  0.000494
5945  292.0  0.000077  0.000491
5946  291.0  0.000076  0.000489
5947  290.0  0.000076  0.000487

[5948 rows x 3 columns]

2.3 构建数据集 

数据集介绍:数据集中提供了火灾温度(Tem1)、一氧化碳浓度(CO 1)、烟雾浓度(Soot 1)随着时间变化的数据

2.3.1 数据集预处理

代码如下:

#数据集处理
dataFrame = data.iloc[:, 1:].copy()
sc = MinMaxScaler(feature_range=(0, 1))for i in ['CO 1', 'Soot 1', 'Tem1']:dataFrame[i] = sc.fit_transform(dataFrame[i].values.reshape(-1, 1))print(dataFrame.shape)

打印输出:(5948, 3)

2.3.2 设置X,y

代码如下:

#设置X、y
width_X = 8
width_y = 1
##取前8个时间段的Tem11、 CO 1、Soot 1为x,第9个时间段的Tem1为y
X = []
y = []in_start = 0
for _, _ in data.iterrows():in_end = in_start + width_Xout_end = in_end + width_yif out_end < len(dataFrame):X_ = np.array(dataFrame.iloc[in_start:in_end, ])y_ = np.array(dataFrame.iloc[in_end:out_end, 0])X.append(X_)y.append(y_)in_start += 1X = np.array(X)
y = np.array(y).reshape(-1, 1, 1)
print(X.shape, y.shape)

打印输出:(5939, 8, 3) (5939, 1, 1)

2.3.3 缺失值检测

代码如下:

##检查数据集中是否有空值
print(np.any(np.isnan(X)))
print(np.any(np.isnan(y)))

打印输出:

False
False

2.3.4 划分数据集

代码如下:

#划分数据集
X_train = torch.tensor(np.array(X[:5000]), dtype=torch.float32)
y_train = torch.tensor(np.array(y[:5000]), dtype=torch.float32)X_test = torch.tensor(np.array(X[5000:]), dtype=torch.float32)
y_test = torch.tensor(np.array(y[5000:]), dtype=torch.float32)
print(X_train.shape, y_train.shape, X_test.shape, y_test.shape)train_dl = DataLoader(TensorDataset(X_train, y_train), batch_size=64, shuffle=False)
test_dl = DataLoader(TensorDataset(X_test, y_test), batch_size=64, shuffle=False)

打印输出:

torch.Size([5000, 8, 3]) torch.Size([5000, 1, 1]) torch.Size([939, 8, 3]) torch.Size([939, 1, 1])

三、模型训练

3.1 构建模型

3.1.1 nn.LSTM()函数详解

3.1.2 模型搭建

代码如下:

#模型训练
##构建模型
class model_lstm(nn.Module):def __init__(self):super(model_lstm, self).__init__()self.lstm0 = nn.LSTM(input_size=3, hidden_size=320, num_layers=1, batch_first=True)self.lstm1 = nn.LSTM(input_size=320, hidden_size=320, num_layers=1, batch_first=True)self.fc0 = nn.Linear(320, 1)def forward(self, x):out, hidden1 = self.lstm0(x)out, _ = self.lstm1(out, hidden1)out = self.fc0(out)return out[:, -1:, :]        #取两个预测值,否则经过lstm会得到8*2个预测model = model_lstm()
print(model)#查看模型的输出数据集格式
print(model(torch.rand(30,8,3)).shape)

打印输出:

torch.Size([30, 1, 1])

3.2 训练函数

代码如下:

##定义训练函数
import copy
def train(train_dl, model, loss_fn, opt, lr_scheduler=None):size = len(train_dl.dataset)num_batches = len(train_dl)train_loss =0        #初始化训练损失和正确率for x, y in train_dl:x, y = x.to(device), y.to(device)#计算预测误差pred = model(x)    #网络输出loss = loss_fn(pred, y)   #计算预测值与真实值之间的差距#反向传播opt.zero_grad()     #梯度归零loss.backward()     #反向传播opt.step()          #每一步自动更新#记录losstrain_loss += loss.item()if lr_scheduler is not None:lr_scheduler.step()print("learning rate = {:.5f}".format(opt.param_groups[0]['lr']), end=" ")train_loss /= num_batchesreturn train_loss

3.3 测试函数

代码如下:

##定义测试函数
def test(dataloader, model, loss_fn):size = len(dataloader.dataset)num_batches = len(dataloader)test_loss = 0with torch.no_grad():for x, y in dataloader:x, y = x.to(device), y.to(device)y_pred = model(x)loss = loss_fn(y_pred, y)test_loss += loss.item()test_loss /= num_batchesreturn test_loss

3.4 正式训练

代码如下:

##正式训练模型
device = "cuda" if torch.cuda.is_available() else "cpu"
print(device)model = model.to(device)
loss_fn = nn.MSELoss()
learn_rate = 1e-1
opt = torch.optim.SGD(model.parameters(), lr=learn_rate, weight_decay=1e-4)
epochs = 50
train_loss = []
test_loss = []
lr_scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(opt, epochs, last_epoch=-1)
best_val = [0, 1e5]
for epoch in range(epochs):model.train()epoch_train_loss = train(train_dl, model, loss_fn, opt, lr_scheduler)model.eval()epoch_test_loss = test(test_dl, model, loss_fn)if best_val[1] > epoch_test_loss:best_val = [epoch, epoch_test_loss]best_model_wst = copy.deepcopy(model.state_dict())train_loss.append(epoch_train_loss)test_loss.append(epoch_test_loss)template = ('Epoch:{:2d}, Train_loss:{:.5f}, Test_loss:{:.5f}')print(template.format(epoch+1, epoch_train_loss, epoch_test_loss))print("="*20, 'Done', "="*20)

训练过程如下:

截取部分

四、模型评估

4.1 Loss图

代码如下:

#模型评估
##LOSS图
plt.figure(figsize=(5, 3),dpi=120)
plt.plot(train_loss, label='LSTM Training Loss')
plt.plot(test_loss, label='LSTM Validation Loss')plt.title('Training and Validation loss')
plt.legend()
plt.show()

打印输出:

4.2 调用模型进行预测

代码如下:

##调用模型进行预测
model.load_state_dict(best_model_wst)
model.to("cpu")
predicted_y_lstm = sc.inverse_transform(model(X_test).detach().numpy().reshape(-1, 1))   #对经过LSTM模型预测得到的数据进行逆值转换,将其转换回原始的数据空间中
y_test_1 = sc.inverse_transform(y_test.reshape(-1, 1))
y_test_one = [i[0] for i in y_test_1]
predicted_y_lstm_one = [i[0] for i in predicted_y_lstm]plt.figure(figsize=(5, 3), dpi=120)
plt.plot(y_test_one[:2000], color='red', label='real_temp')
plt.plot(predicted_y_lstm_one[:2000], color='blue', label='prediction')plt.title('Title')
plt.xlabel('X')
plt.ylabel('Y')
plt.legend()
plt.show()

输出如下:

4.3 R^2值评估

代码如下:

RMSE_lstm = metrics.mean_squared_error(predicted_y_lstm_one, y_test_1) ** 0.5
R2_lstm = metrics.r2_score(predicted_y_lstm_one, y_test_1)
print('均方根误差: %.5f' % RMSE_lstm)
print('R2: %.5f' % R2_lstm)

打印输出:

均方根误差: 6.80718
R2: 0.84027

五、拔高尝试

使用第1~8个时刻的数据预测第9~10个时刻的温度数据,实际上也是把原来的单步预测修改为多步预测,主要改动部分在模型输出部分

这是原始模型

修改后的代码

预测代码如下:

#拔高练习
test_1 = torch.tensor(dataFrame.iloc[:8 , ].values,dtype=torch.float32).reshape(1,-1,3)
pred_ = model(test_1)
pred_ = np.round(sc.inverse_transform(pred_.detach().numpy().reshape(1,-1)).reshape(-1),2)  #NumPy中的round函数将结果四舍五入保留两位小数
real_tem = data.Tem1.iloc[:2].values
print(f"第9~10时刻的温度预测:",  pred_)
print("第9~10时刻的真实温度:",  real_tem)

打印输出:

第9~10时刻的温度预测: [33.61 32.73]
第9~10时刻的真实温度: [25. 25.]


总结

  • 学习对比了LSTM与RNN的区别,以及LSTM的优势
  • 实际操作搭建了LSTM模型,运行也达到了预期结果
  • 对于时序数据导出以及处理步骤
  • 拔高部分实现了将LSTM修改为多步输出

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

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

相关文章

【运维】磁盘满了怎么办?如何快速找到占用空间的文件和腾出空间

机器用久了&#xff0c;很容易生成很多临时或者无用的文件&#xff0c;占用大量空间造成磁盘不够用。尤其是服务器&#xff0c;当磁盘不够用时&#xff0c;系统会出现莫名其妙的问题&#xff0c;数据库可能会造成数据损坏。此时快速定位可以删除的大文件并及时释放空间&#xf…

AI Earth——1990-2022年全国月度气象数据检索应用app

应用结果 代码 #导入安装包 import os import json import datetime import streamlit as st import streamlit.components.v1 as components import traceback from PIL import Imageimport aie#读取当前目录的内容 current_work_dir = os.path.dirname(__file__) #添加地图…

leetcode--二叉树中的最大路径和

leetcode地址&#xff1a;二叉树中的最大路径和 二叉树中的 路径 被定义为一条节点序列&#xff0c;序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点&#xff0c;且不一定经过根节点。 路径和 是路径中各节点值的总…

Nginx+Tomcat群集

**Nginx Tomcat 集群** Nginx 和 Tomcat 集群的组合是一种常见且强大的架构方案&#xff0c;旨在实现高可用性、可扩展性和高性能的 Web 应用服务。 Nginx 是一款轻量级的高性能 Web 服务器和反向代理服务器。它能够高效地处理静态资源请求&#xff0c;并将动态请求转发到后…

pytest-yaml-sanmu(六):YAML数据驱动测试

如果说 pytest 中哪些标记使用得最多&#xff0c;那无疑是 parametrize 了&#xff0c; 它为用例实现了参数化测试的能力&#xff0c;进而实现了数据驱动测试的能力。 1. 使用标记 parametrize 的使用需要提高两个内容&#xff1a; 参数名 参数值 pytest 在执行用例时&…

6元/年英国Giffgaff卡申请和使用

官网&#xff1a;https://www.giffgaff.com/freesim-international 今天和大家分享一款来自英国的电话卡——Giffgaff&#xff0c;它能够在大陆正常使用&#xff0c;并且保号的费用也十分便宜&#xff0c;大约6元/年。自己免费申请的卡已经激活成功&#xff0c;将过程与大家分…

亚信安全新一代终端安全TrustOne2024年重磅升级

以极简新主义为核心&#xff0c;亚信安全新一代终端安全TrustOne自2023年发布以来&#xff0c;带动了数字化终端安全的革新。60%&#xff0c;安装部署及管理效率的提升&#xff1b;50%&#xff0c;安全管理资源的节省&#xff1b;100%&#xff0c;信创非信创场景的全覆盖。Trus…

FastReport 指定sql 和修改 数据库连接地址的 工具类 :FastReportHelper

FastReport 指定sql 和修改 数据库连接地址的 工具类 &#xff1a;FastReportHelper 介绍核心代码&#xff1a;完整代码&#xff1a; 介绍 在FastReport中&#xff0c;经常会遇到需要给 sql 加条件的情况&#xff0c;或者给数据库地址做更换。 &#xff08;废话不多说&#x…

java之循环练习题

思路分析&#xff1a; 代码&#xff1a; public static void main(String[] args) {int sum0;for (int i1;i<100;i){for (int j1;j<i;j) {sum j;}}System.out.println(sum);} 结果为&#xff1a;

DeepViT:字节提出深层ViT的训练策略 | 2021 arxiv

作者发现深层ViT出现的注意力崩溃问题&#xff0c;提出了新颖的Re-attention机制来解决&#xff0c;计算量和内存开销都很少&#xff0c;在增加ViT深度时能够保持性能不断提高 来源&#xff1a;晓飞的算法工程笔记 公众号 论文: DeepViT: Towards Deeper Vision Transformer 论…

提升爬虫OCR识别率:解决嘈杂验证码问题

引言 在数据抓取和网络爬虫技术中&#xff0c;验证码是常见的防爬措施&#xff0c;特别是嘈杂文本验证码。处理嘈杂验证码是一个复杂的问题&#xff0c;因为这些验证码故意设计成难以自动识别。本文将介绍如何使用OCR技术提高爬虫识别嘈杂验证码的准确率&#xff0c;并结合实际…

面向对象的程序设计设计思想(解决问题所需要的类),面向过程的程序设计思想(解决问题的步骤)

一、引言 面向对象思想是现代编程语言的主流编程思想&#xff0c;除了C语言外&#xff0c;其他的主流编程语言&#xff0c;无论是脚本的还是非脚本的&#xff0c;基本上都引入了面向对象这一设计思想&#xff0c;面向对象设计思想是怎样的&#xff1f;为什么现在的编程语言大都…

模型驱动开发(Model-Driven Development,MDD):提高软件开发效率与一致性的利器

目录 前言1. 模型驱动开发的原理1.1 什么是模型驱动开发1.2 MDD的核心思想 2. 模型驱动开发的优势2.1 提高开发效率2.2 确保代码一致性2.3 促进沟通和协作2.4 方便维护和扩展 3. 实现模型驱动开发的方法3.1 选择合适的建模工具3.1.1 UML3.1.2 BPMN3.1.3 SysML 3.2 建模方法3.2.…

大学生竞赛管理系统-计算机毕业设计源码37276

大学生竞赛管理系统的设计与实现 摘 要 随着教育信息化的不断发展&#xff0c;大学生竞赛已成为高校教育的重要组成部分。传统的竞赛组织和管理方式存在着诸多问题&#xff0c;如信息不透明、效率低下、管理不便等。为了解决这些问题&#xff0c;提高竞赛组织和管理效率&#x…

K8S 上部署大数据相关组件

文章目录 一、前言二、Redis 一、前言 Artifact Hub 是一个专注于云原生应用的集中式搜索和发布平台。它旨在简化开发者在 CNCF&#xff08;Cloud Native Computing Foundation&#xff09;项目中寻找、安装和分享包与配置的过程。用户可以通过这个平台方便地发现、安装各类云原…

用SurfaceView实现落花动画效果

上篇文章 Android子线程真的不能刷新UI吗&#xff1f;(一&#xff09;复现异常 中可以看出子线程更新main线程创建的View&#xff0c;会抛出异常。SurfaceView不依赖main线程&#xff0c;可以直接使用自己的线程控制绘制逻辑。具体代码怎么实现了&#xff1f; 这篇文章用Surfa…

vscode启用项目后,没有触发debugger

启动项目后在debugger时&#xff0c;一直不走断点&#xff0c;重启vscode和电脑&#xff0c;打开其他vscode项目&#xff0c;都不行 1.F12点击设置 2.然后取消忽略列表的勾选即可。

【力扣高频题】042.接雨水问题

上一篇我们通过采用 双指针 的方法解决了 经典 容器盛水 问题 &#xff0c;本文我们接着来学习一道在面试中极大概率会被考到的经典题目&#xff1a;接雨水 问题 。 42. 接雨水 给定 n 个非负整数&#xff0c;表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子…

Java-Redis-Clickhouse-Jenkins-MybatisPlus-Zookeeper-vscode-Docker-jdbc

文章目录 Clickhouse基础实操windows docker desktop 下载clickhousespringboot项目配置clickhouse Redis谈下你对Redis的了解&#xff1f;Redis一般都有哪些使用的场景&#xff1f;Redis有哪些常见的功能&#xff1f;Redis支持的数据类型有哪些&#xff1f;Redis为什么这么快…

第一个ffmpeg程序

在进行使用ffmpeg进行编写程序时&#xff0c;首先要记得进行注册设备&#xff08;avdevice_register_all &#xff09;&#xff0c;程序运行时&#xff0c;只需要注册一次就可以 avdevice_register_all 是 FFmpeg 多媒体处理库中的一个函数&#xff0c;其作用是注册所有可用的音…