PyTorch音频分类实战(完整代码)

news2024/11/24 16:44:32

《SwanLab机器学习实战教程》是一个主打「开箱即用」的AI训练系列教程,我们致力于提供完善的数据集、源代码、实验记录以及环境安装方式,手把手帮助你跑起训练,解决问题。

音频分类任务是指将音频信号按照其内容的类别归属进行划分。例如,区分一段音频是音乐、语音、环境声音(如鸟鸣、雨声、机器运转声)还是动物叫声等。其目的是通过自动分类的方式,高效地对大量音频数据进行组织、检索和理解。

在这里插入图片描述

在现在音频分类的应用场景,比较多的是在音频标注、音频推荐这一块。同时,这也是一个非常好的入门音频模型训练的任务。

在本文中,我们会基于PyTorch框架,使用 ResNet系列模型在 GTZAN 数据集上进行训练,同时使用SwanLab监控训练过程、评估模型效果。

  • Github:https://github.com/Zeyi-Lin/PyTorch-Audio-Classification
  • 数据集:https://pan.baidu.com/s/14CTI_9MD1vXCqyVxmAbeMw?pwd=1a9e 提取码: 1a9e
  • SwanLab实验日志:https://swanlab.cn/@ZeyiLin/PyTorch_Audio_Classification-simple/charts
  • 更多实验日志:https://swanlab.cn/@ZeyiLin/PyTorch_Audio_Classification/charts

1. 音频分类逻辑

本教程对音频分类任务的逻辑如下:

  1. 载入音频数据集,数据集为音频WAV文件与对应的标签
  2. 以8:2的比例划分训练集和测试集
  3. 使用`torchaudio`库,将音频文件转换为梅尔频谱图,本质将其转换为图像分类任务
  4. 使用ResNet模型对梅尔频谱图进行训练迭代
  5. 使用SwanLab记录训练和测试阶段的loss、acc变化,并对比不同实验之间的效果差异

2. 环境安装

本案例基于Python>=3.8,请在您的计算机上安装好Python。

我们需要安装以下这几个Python库:

torch
torchvision
torchaudio
swanlab
pandas
scikit-learn

一键安装命令:

pip install torch torchvision torchaudio swanlab pandas scikit-learn

3. GTZAN数据集准备

本任务使用的数据集为GTZAN,这是一个在音乐流派识别研究中常用的公开数据集。GTZAN数据集包含 1000 个音频片段,每个音频片段的时长为 30 秒,共分为 10 种音乐流派:包括布鲁斯(Blues)、古典(Classical)、乡村(Country)、迪斯科(Disco)、嘻哈(Hip Hop)、爵士(Jazz)、金属(Metal)、流行(Pop)、雷鬼(Reggae)、摇滚(Rock),且每种流派都有 100 个音频片段。

在这里插入图片描述

GTZAN数据集是在 2000-2001 年从各种来源收集的,包括个人 CD、收音机、麦克风录音等,代表了各种录音条件下的声音。

数据下载方式(大小1.4GB):

  1. 百度网盘下载:链接: https://pan.baidu.com/s/14CTI_9MD1vXCqyVxmAbeMw?pwd=1a9e 提取码: 1a9e
  2. 通过Kaggle下载:https://www.kaggle.com/datasets/andradaolteanu/gtzan-dataset-music-genre-classification
  3. 在Hyper超神经网站下载BT种子进行下载:https://hyper.ai/cn/datasets/32001

注意,数据集中有一个音频是损坏的,在百度网盘版本里已经将其剔除。

下载完成后,解压到项目根目录下即可。

4. 生成数据集CSV文件

我们将数据集中的音频文件路径和对应的标签,处理成一个audio_dataset.csv文件,其中第一列为文件路径,第二列为标签:

(这一部分先不执行,在完整代码里会带上)

import os
import pandas as pd

def create_dataset_csv():
    # 数据集根目录
    data_dir = './GTZAN/genres_original'
    data = []
    
    # 遍历所有子目录
    for label in os.listdir(data_dir):
        label_dir = os.path.join(data_dir, label)
        if os.path.isdir(label_dir):
            # 遍历子目录中的所有wav文件
            for audio_file in os.listdir(label_dir):
                if audio_file.endswith('.wav'):
                    audio_path = os.path.join(label_dir, audio_file)
                    data.append([audio_path, label])
    
    # 创建DataFrame并保存为CSV
    df = pd.DataFrame(data, columns=['path', 'label'])
    df.to_csv('audio_dataset.csv', index=False)
    return df


# 生成或加载数据集CSV文件
if not os.path.exists('audio_dataset.csv'):
    df = create_dataset_csv()
else:
    df = pd.read_csv('audio_dataset.csv')

处理后,你会在根目录下看到一个audio_dataset.csv文件:

在这里插入图片描述

5. 配置训练跟踪工具SwanLab

SwanLab 是一款开源、轻量的 AI 实验跟踪工具,提供了一个跟踪、比较、和协作实验的平台。SwanLab 提供了友好的 API 和漂亮的界面,结合了超参数跟踪、指标记录、在线协作、实验链接分享等功能,让您可以快速跟踪 AI 实验、可视化过程、记录超参数,并分享给伙伴。

在这里插入图片描述

SwanLab的使用方式类似Weights & Biases + Tensorboard,既支持云端集中式地管理你的训练(W&B方式),也可以快捷地启动本地离线看板(Tensorboard方式)

配置SwanLab的方式很简单:

  1. 注册一个账号:https://swanlab.cn
  2. 在安装好swanlab后(pip install swanlab),登录:
swanlab login

在提示输入API Key时,去设置页面复制API Key,粘贴后按回车即可。

在这里插入图片描述

6. 完整代码

开始训练时的目录结构:

|--- train.py
|--- GTZAN

train.py做的事情包括:

  1. 生成数据集csv文件
  2. 加载数据集和resnet18模型(ImageNet预训练)
  3. 训练20个epoch,每个epoch进行训练和评估
  4. 记录loss和acc,以及学习率的变化情况,在swanlab中可视化

train.py:

import torch
import torch.nn as nn
import torch.optim as optim
import torchaudio
import torchvision.models as models
from torch.utils.data import Dataset, DataLoader
import os
import pandas as pd
from sklearn.model_selection import train_test_split
import swanlab


def create_dataset_csv():
    # 数据集根目录
    data_dir = './GTZAN/genres_original'
    data = []
    
    # 遍历所有子目录
    for label in os.listdir(data_dir):
        label_dir = os.path.join(data_dir, label)
        if os.path.isdir(label_dir):
            # 遍历子目录中的所有wav文件
            for audio_file in os.listdir(label_dir):
                if audio_file.endswith('.wav'):
                    audio_path = os.path.join(label_dir, audio_file)
                    data.append([audio_path, label])
    
    # 创建DataFrame并保存为CSV
    df = pd.DataFrame(data, columns=['path', 'label'])
    df.to_csv('audio_dataset.csv', index=False)
    return df

# 自定义数据集类
class AudioDataset(Dataset):
    def __init__(self, df, resize, train_mode=True):
        self.audio_paths = df['path'].values
        # 将标签转换为数值
        self.label_to_idx = {label: idx for idx, label in enumerate(df['label'].unique())}
        self.labels = [self.label_to_idx[label] for label in df['label'].values]
        self.resize = resize
        self.train_mode = train_mode  # 添加训练模式标志
    def __len__(self):
        return len(self.audio_paths)
    
    def __getitem__(self, idx):
        # 加载音频文件
        waveform, sample_rate = torchaudio.load(self.audio_paths[idx])
        
        # 将音频转换为梅尔频谱图
        transform = torchaudio.transforms.MelSpectrogram(
            sample_rate=sample_rate,
            n_fft=2048,
            hop_length=640,
            n_mels=128
        )
        mel_spectrogram = transform(waveform)

        # 确保数值在合理范围内
        mel_spectrogram = torch.clamp(mel_spectrogram, min=0)
        
        # 转换为3通道图像格式 (为了适配ResNet)
        mel_spectrogram = mel_spectrogram.repeat(3, 1, 1)
        
        # 确保尺寸一致
        resize = torch.nn.AdaptiveAvgPool2d((self.resize, self.resize))
        mel_spectrogram = resize(mel_spectrogram)
        
        return mel_spectrogram, self.labels[idx]

# 修改ResNet模型
class AudioClassifier(nn.Module):
    def __init__(self, num_classes):
        super(AudioClassifier, self).__init__()
        # 加载预训练的ResNet
        self.resnet = models.resnet18(weights=models.ResNet18_Weights.IMAGENET1K_V1)
        # 修改最后的全连接层
        self.resnet.fc = nn.Linear(512, num_classes)
        
    def forward(self, x):
        return self.resnet(x)

# 训练函数
def train_model(model, train_loader, val_loader, criterion, optimizer, num_epochs, device):
    
    for epoch in range(num_epochs):
        model.train()
        running_loss = 0.0
        correct = 0
        total = 0
        
        for i, (inputs, labels) in enumerate(train_loader):
            inputs, labels = inputs.to(device), labels.to(device)
            
            outputs = model(inputs)
            loss = criterion(outputs, labels)
            
            loss.backward()

            optimizer.step()
            optimizer.zero_grad()
            
            running_loss += loss.item()
            
            _, predicted = outputs.max(1)
            total += labels.size(0)
            correct += predicted.eq(labels).sum().item()
        
        train_loss = running_loss/len(train_loader)
        train_acc = 100.*correct/total
        
        # 验证阶段
        model.eval()
        val_loss = 0.0
        correct = 0
        total = 0
        with torch.no_grad():
            for inputs, labels in val_loader:
                inputs, labels = inputs.to(device), labels.to(device)
                outputs = model(inputs)
                loss = criterion(outputs, labels)
                
                val_loss += loss.item()
                _, predicted = outputs.max(1)
                total += labels.size(0)
                correct += predicted.eq(labels).sum().item()
        
        val_loss = val_loss/len(val_loader)
        val_acc = 100.*correct/total
        
        current_lr = optimizer.param_groups[0]['lr']
        
        # 记录训练和验证指标
        swanlab.log({
            "train/loss": train_loss,
            "train/acc": train_acc,
            "val/loss": val_loss,
            "val/acc": val_acc,
            "train/epoch": epoch,
            "train/lr": current_lr
        })
            
        print(f'Epoch {epoch+1}:')
        print(f'Train Loss: {train_loss:.4f} | Train Acc: {train_acc:.2f}%')
        print(f'Val Loss: {val_loss:.4f} | Val Acc: {val_acc:.2f}%')
        print(f'Learning Rate: {current_lr:.6f}')

# 主函数
def main():
    # 设置设备
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    
    run = swanlab.init(
        project="PyTorch_Audio_Classification-simple",
        experiment_name="resnet18",
        config={
            "batch_size": 16,
            "learning_rate": 1e-4,
            "num_epochs": 20,
            "resize": 224,
        },
    )
    
    # 生成或加载数据集CSV文件
    if not os.path.exists('audio_dataset.csv'):
        df = create_dataset_csv()
    else:
        df = pd.read_csv('audio_dataset.csv')
    
    # 划分训练集和验证集
    train_df = pd.DataFrame()
    val_df = pd.DataFrame()
    
    for label in df['label'].unique():
        label_df = df[df['label'] == label]
        label_train, label_val = train_test_split(label_df, test_size=0.2, random_state=42)
        train_df = pd.concat([train_df, label_train])
        val_df = pd.concat([val_df, label_val])
    
    # 创建数据集和数据加载器 
    train_dataset = AudioDataset(train_df, resize=run.config.resize, train_mode=True)
    val_dataset = AudioDataset(val_df, resize=run.config.resize, train_mode=False)
    
    train_loader = DataLoader(train_dataset, batch_size=run.config.batch_size, shuffle=True)
    val_loader = DataLoader(val_dataset, batch_size=1, shuffle=False)
    
    # 创建模型
    num_classes = len(df['label'].unique())  # 根据实际分类数量设置
    print("num_classes", num_classes)
    model = AudioClassifier(num_classes).to(device)
    
    # 定义损失函数和优化器
    criterion = nn.CrossEntropyLoss()
    optimizer = optim.Adam(model.parameters(), lr=run.config.learning_rate)  
    
    # 训练模型
    train_model(model, train_loader, val_loader, criterion, optimizer, num_epochs=run.config.num_epochs, device=device)

if __name__ == "__main__":
    main()

看到下面的输出,则代表训练开始:

在这里插入图片描述

访问打印的swanlab链接,可以看到训练的全过程:

在这里插入图片描述

可以看到Reset18模型,且不加任何策略的条件下,在训练集的准确率为99.5%,验证集的准确率最高为71.5%,val loss在第3个epoch开始反而在上升,呈现「过拟合」的趋势。

7. 进阶代码

下面是我训出验证集准确率87.5%的实验,具体策略包括:

  1. 将模型换成resnext101_32x8d
  2. 将梅尔顿图的resize提高到512
  3. 增加warmup策略
  4. 增加时间遮蔽、频率屏蔽、高斯噪声、随机响度这四种数据增强策略
  5. 增加学习率梯度衰减策略

在这里插入图片描述

进阶代码(需要24GB显存,如果要降低显存消耗的话,可以调低batch_size):

import torch
import torch.nn as nn
import torch.optim as optim
import torchaudio
import torchvision.models as models
from torch.utils.data import Dataset, DataLoader
import os
import pandas as pd
from sklearn.model_selection import train_test_split
import swanlab
import random
import numpy as np

# 设置随机种子
def set_seed(seed=42):
    random.seed(seed)
    os.environ['PYTHONHASHSEED'] = str(seed)
    np.random.seed(seed)
    torch.manual_seed(seed)
    torch.cuda.manual_seed(seed)
    torch.cuda.manual_seed_all(seed)
    torch.backends.cudnn.deterministic = True
    torch.backends.cudnn.benchmark = False

def create_dataset_csv():
    # 数据集根目录
    data_dir = './GTZAN/genres_original'
    data = []
    
    # 遍历所有子目录
    for label in os.listdir(data_dir):
        label_dir = os.path.join(data_dir, label)
        if os.path.isdir(label_dir):
            # 遍历子目录中的所有wav文件
            for audio_file in os.listdir(label_dir):
                if audio_file.endswith('.wav'):
                    audio_path = os.path.join(label_dir, audio_file)
                    data.append([audio_path, label])
    
    # 创建DataFrame并保存为CSV
    df = pd.DataFrame(data, columns=['path', 'label'])
    df.to_csv('audio_dataset.csv', index=False)
    return df

# 自定义数据集类
class AudioDataset(Dataset):
    def __init__(self, df, resize, train_mode=True):
        self.audio_paths = df['path'].values
        # 将标签转换为数值
        self.label_to_idx = {label: idx for idx, label in enumerate(df['label'].unique())}
        self.labels = [self.label_to_idx[label] for label in df['label'].values]
        self.resize = resize
        self.train_mode = train_mode  # 添加训练模式标志
    def __len__(self):
        return len(self.audio_paths)
    
    def __getitem__(self, idx):
        # 加载音频文件
        waveform, sample_rate = torchaudio.load(self.audio_paths[idx])
        
        # 将音频转换为梅尔频谱图
        transform = torchaudio.transforms.MelSpectrogram(
            sample_rate=sample_rate,
            n_fft=2048,
            hop_length=640,
            n_mels=128
        )
        mel_spectrogram = transform(waveform)
        
        # 仅在训练模式下进行数据增强
        if self.train_mode:
            # 1. 时间遮蔽 (Time Masking):通过随机选择一个时间步,然后遮蔽掉20个时间步
            time_mask = torchaudio.transforms.TimeMasking(time_mask_param=20)
            mel_spectrogram = time_mask(mel_spectrogram)
            
            # 2. 频率遮蔽 (Frequency Masking):通过随机选择一个频率步,然后遮蔽掉20个频率步
            freq_mask = torchaudio.transforms.FrequencyMasking(freq_mask_param=20)
            mel_spectrogram = freq_mask(mel_spectrogram)
            
            # 3. 随机增加高斯噪声
            if random.random() < 0.5:
                noise = torch.randn_like(mel_spectrogram) * 0.01
                mel_spectrogram = mel_spectrogram + noise
            
            # 4. 随机调整响度
            if random.random() < 0.5:
                gain = random.uniform(0.8, 1.2)
                mel_spectrogram = mel_spectrogram * gain

        # 确保数值在合理范围内
        mel_spectrogram = torch.clamp(mel_spectrogram, min=0)
        
        # 转换为3通道图像格式 (为了适配ResNet)
        mel_spectrogram = mel_spectrogram.repeat(3, 1, 1)
        
        # 确保尺寸一致
        resize = torch.nn.AdaptiveAvgPool2d((self.resize, self.resize))
        mel_spectrogram = resize(mel_spectrogram)
        
        return mel_spectrogram, self.labels[idx]

# 修改ResNet模型
class AudioClassifier(nn.Module):
    def __init__(self, num_classes):
        super(AudioClassifier, self).__init__()
        # 加载预训练的ResNet
        self.resnet = models.resnext101_32x8d(weights=models.ResNeXt101_32X8D_Weights.IMAGENET1K_V1)
        # 修改最后的全连接层
        self.resnet.fc = nn.Linear(2048, num_classes)
        
    def forward(self, x):
        return self.resnet(x)

# 训练函数
def train_model(model, train_loader, val_loader, criterion, optimizer, scheduler, num_epochs, device, run):
    for epoch in range(num_epochs):
        model.train()
        running_loss = 0.0
        correct = 0
        total = 0
        
        # 前5个epoch进行warmup
        if epoch < 5:
            warmup_factor = (epoch + 1) / 5
            for param_group in optimizer.param_groups:
                param_group['lr'] = run.config.learning_rate * warmup_factor
        
        # optimizer.zero_grad()  # 移到循环外部
        
        for i, (inputs, labels) in enumerate(train_loader):
            inputs, labels = inputs.to(device), labels.to(device)
            
            outputs = model(inputs)
            loss = criterion(outputs, labels)
            loss.backward()
            
            optimizer.step()
            optimizer.zero_grad()
            
            running_loss += loss.item()
            
            _, predicted = outputs.max(1)
            total += labels.size(0)
            correct += predicted.eq(labels).sum().item()
        
        train_loss = running_loss
        train_acc = 100.*correct/total
        
        # 验证阶段
        model.eval()
        val_loss = 0.0
        correct = 0
        total = 0
        with torch.no_grad():
            for inputs, labels in val_loader:
                inputs, labels = inputs.to(device), labels.to(device)
                outputs = model(inputs)
                loss = criterion(outputs, labels)
                
                val_loss += loss.item()
                _, predicted = outputs.max(1)
                total += labels.size(0)
                correct += predicted.eq(labels).sum().item()
        
        val_loss = val_loss/len(val_loader)
        val_acc = 100.*correct/total
        
        # 只在warmup结束后使用学习率调度器
        if epoch >= 5:
            scheduler.step()
        current_lr = optimizer.param_groups[0]['lr']
        
        # 记录训练和验证指标
        swanlab.log({
            "train/loss": train_loss,
            "train/acc": train_acc,
            "val/loss": val_loss,
            "val/acc": val_acc,
            "train/epoch": epoch,
            "train/lr": current_lr
        })
            
        print(f'Epoch {epoch+1}:')
        print(f'Train Loss: {train_loss:.4f} | Train Acc: {train_acc:.2f}%')
        print(f'Val Loss: {val_loss:.4f} | Val Acc: {val_acc:.2f}%')
        print(f'Learning Rate: {current_lr:.6f}')

# 主函数
def main():
    # 设置随机种子
    set_seed(42)
    
    # 设置设备
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    
    run = swanlab.init(
        project="PyTorch_Audio_Classification-simple",
        experiment_name="😄resnext101_32x8d",
        config={
            "batch_size": 16,
            "learning_rate": 1e-4,
            "num_epochs": 30,
            "resize": 512,
            "weight_decay": 0  # 添加到配置中
        },
    )
    
    # 生成或加载数据集CSV文件
    if not os.path.exists('audio_dataset.csv'):
        df = create_dataset_csv()
    else:
        df = pd.read_csv('audio_dataset.csv')
    
    # 划分训练集和验证集
    train_df = pd.DataFrame()
    val_df = pd.DataFrame()
    
    for label in df['label'].unique():
        label_df = df[df['label'] == label]
        label_train, label_val = train_test_split(label_df, test_size=0.2, random_state=42)
        train_df = pd.concat([train_df, label_train])
        val_df = pd.concat([val_df, label_val])
    
    # 创建数据集和数据加载器 
    train_dataset = AudioDataset(train_df, resize=run.config.resize, train_mode=True)
    val_dataset = AudioDataset(val_df, resize=run.config.resize, train_mode=False)
    
    train_loader = DataLoader(train_dataset, batch_size=run.config.batch_size, shuffle=True)
    val_loader = DataLoader(val_dataset, batch_size=1, shuffle=False)
    
    # 创建模型
    num_classes = len(df['label'].unique())  # 根据实际分类数量设置
    model = AudioClassifier(num_classes).to(device)
    
    # 定义损失函数和优化器
    criterion = nn.CrossEntropyLoss()
    optimizer = optim.Adam(
        model.parameters(), 
        lr=run.config.learning_rate,
        weight_decay=run.config.weight_decay
    )  # Adam优化器
    
    # 添加学习率调度器
    scheduler = optim.lr_scheduler.StepLR(
        optimizer,
        step_size=10,  # 在第10个epoch衰减
        gamma=0.1,     # 衰减率为0.1
        verbose=True
    )
    
    # 训练模型
    train_model(model, train_loader, val_loader, criterion, optimizer, scheduler, 
                num_epochs=run.config.num_epochs, device=device, run=run)
    

if __name__ == "__main__":
    main()

在这里插入图片描述

可以看到提升的非常明显

期待有训练师能把eval acc刷上90!

8. 相关链接

  • Github:https://github.com/Zeyi-Lin/PyTorch-Audio-Classification
  • 数据集:https://pan.baidu.com/s/14CTI_9MD1vXCqyVxmAbeMw?pwd=1a9e 提取码: 1a9e
  • SwanLab实验日志:https://swanlab.cn/@ZeyiLin/PyTorch_Audio_Classification-simple/charts
  • 更多实验日志:https://swanlab.cn/@ZeyiLin/PyTorch_Audio_Classification/charts
  • SwanLab官网:https://swanlab.cn

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

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

相关文章

产品经理如何使用项目管理软件推进复杂项目按时上线

前言 相信很多产品同学或多或少都有过这样的经历&#xff1a;平时没有听到任何项目延期风险&#xff0c;但到了计划时间却迟迟无法提测……评审时没有任何argue&#xff0c;提测后发现开发的功能不是自己想要的……费劲九牛二虎之力终于让项目上线了&#xff0c;然而发现成果达…

nginx配置自签证书

文章目录 一.OpenSSL下载二.生成证书三.nginx配置 四.常见错误4.1 the event "ngx_master_xxxx" was not signaled for 5s 一.OpenSSL下载 # ubuntu sudo apt-get update sudo apt-get install openssl二.生成证书 # 创建存放证书的目录 mkdir /usr/local/mydata/s…

【elkb】kibana后台删除索引

打开kibana后台 点击 Management ---> Index Management 找到要删除的所以点击 点击delete index 删除成功

【Web前端】从回调到现代Promise与Async/Await

异步编程是一种让程序能够在等待某些操作完成的同时继续执行其他任务的关键技术&#xff0c;打破了传统编程中顺序执行代码的束缚。这种编程范式允许开发者构建出能够即时响应用户操作、高效处理网络请求和资源加载的应用程序。通过异步编程&#xff0c;JavaScript 能够在执行耗…

11.9.2024刷华为

文章目录 HJ31 单词倒排HJ32 密码提取语法知识记录 傻逼OD题目又不全又要收费&#xff0c;看毛线&#xff0c;莫名奇妙 HW这叼机构别搁这儿害人得不得&#xff1f; 我觉得我刷完原来的题目 过一遍华为机考的ED卷出处&#xff0c;就行了 HJ31 单词倒排 游戏本做过了好像 HJ3…

【Qt聊天室客户端】登录窗口

1. 验证码 具体实现 登录界面中创建验证码图片空间&#xff0c;并添加到布局管理器中 主要功能概述&#xff08;创建一个verifycodewidget类专门实现验证码操作&#xff09; 详细代码 // 头文件#ifndef VERIFYCODEWIDGET_H #define VERIFYCODEWIDGET_H#include <QWidget>…

如何判断 Hive 表是内部表还是外部表

在使用 Apache Hive 进行大数据处理时&#xff0c;理解表的类型&#xff08;内部表或外部表&#xff09;对于数据管理和维护至关重要。本篇文章将详细介绍如何判断 Hive 表是内部表还是外部表&#xff0c;并提供具体的操作示例。 目录 Hive 表的类型简介判断表类型的方法 方法…

初始JavaEE篇 —— 文件操作与IO

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a;JavaEE 目录 文件介绍 Java标准库中提供操作文件的类 文件系统操作 File类的介绍 File类的使用 文件内容操作 二进制文件的读写操作…

推荐一个Star超过2K的.Net轻量级的CMS开源项目

推荐一个具有模块化和可扩展的架构的CMS开源项目。 01 项目简介 Piranha CMS是一个轻量级且跨平台的CMS库&#xff0c;专为.NET 8设计。 该项目提供多种模板&#xff0c;具备CMS基本功能&#xff0c;也有空模板方便从头开始构建新网站&#xff0c;甚至可以作为移动应用的后端…

【react-router-dom】你可能要知道的关于v6.x自定义权限路由的实现

路由权限&#xff0c;是webapp或者说后台管理都会需要的业务功能。现在对react-routerv6.x的路由库&#xff0c;封装一个简易的权限路由&#xff0c;实现思路: 后台登录效果 代码实现 思路就是对路由表迭代出来的路由&#xff0c;用一个HOC来进行拦截&#xff0c;在真实进入路…

贪心算法day05(k次取反后最大数组和 田径赛马)

目录 1.k次取反后最大化的数组和 2.按身高排序 3.优势洗牌 1.k次取反后最大化的数组和 题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 代码&#xff1a; class Solution {public int largestSumAfterKNegations(int[] nums, int k) {//如…

AI(11)-海报制作

1-画板 2-【矩形工具】 130x20&#xff1a;2个 100x10&#xff1a;1个 200x30&#xff1a;1个 3-参数调整 3-1-【颜色&#xff1a;65&#xff0c;10&#xff0c;40&#xff0c;0】&#xff0c;【无描边】 4-【shift】【倾斜工具】-调整矩形形状 4-1-单独调整一个矩形 4-2…

cherno引擎课 -

感谢b站星云图形的翻译&#xff1a;【双语】【最佳游戏引擎教程实战】【入门】&#xff08;1&#xff09;&#xff1a;Introducing the GAME ENGINE series!_哔哩哔哩_bilibili Introducing the GAMEENGINE series 希望&#xff1a;它是一个制作(互动)3D实时渲染应用程序的引…

Linux git-bash配置

参考资料 命令提示符Windows下的Git Bash配置&#xff0c;提升你的终端操作体验WindowsTerminal添加git-bash 目录 一. git-bash配置1.1 解决中文乱码1.2 修改命令提示符 二. WindowsTerminal配置git-bash2.1 添加git-bash到WindowsTerminal2.2 解决删除时窗口闪烁问题 三. VS…

秃姐学AI系列之:GRU——门控循环单元 | LSTM——长短期记忆网络

RNN存在的问题 因为RNN模型的BPTT反向传导的链式求导&#xff0c;导致需要反复乘以一个也就是说会出现指数级别的问题&#xff1a; 梯度爆炸&#xff1a;如果的话&#xff0c;那么连乘的结果可能会快速增长&#xff0c;导致梯度爆炸梯度消失&#xff1a;如果的话&#xff0c;…

Java面试要点06 - static关键字、静态属性与静态方法

本文目录 一、引言二、静态属性&#xff08;Static Fields&#xff09;三、静态方法&#xff08;Static Methods&#xff09;四、静态代码块&#xff08;Static Blocks&#xff09;五、静态内部类&#xff08;Static Nested Classes&#xff09;六、静态导入&#xff08;Static…

【测试】【Debug】vscode pytest 找不到测试用例测试文件 行号部位没有绿色箭头

出现这种情况首先检查&#xff1a; 是否安装pytest点击vscode的这个图标如果其中都是空的&#xff0c;没有识别上&#xff0c;并且写好的.py测试文件的行号前面没有运行符号&#xff0c;要检查名称是否按照pytest的要求写&#xff0c;不然会识别不到。 命名规则注意&#xff1…

区块链技术在电子政务中的应用

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 区块链技术在电子政务中的应用 区块链技术在电子政务中的应用 区块链技术在电子政务中的应用 引言 区块链技术概述 定义与原理 发…

【Java Web】Servlet

文章目录 Servlet APIServlet生命周期Servlet配置注解配置文件全局参数 Servlet获得内置对象升级示例3 Servlet 是一种用于构建 Java 服务器端应用程序的技术&#xff0c;允许开发者创建动态的 web 内容。Servlets 在 Java 平台上运行&#xff0c;并由 Java Servlet API 提供支…

linux命令详解,存储管理相关

存储管理 一、内存使用量&#xff0c;free free 命令是一个用于显示系统中物理内存&#xff08;RAM&#xff09;和交换空间&#xff08;swap&#xff09;使用情况的工具 free -m free -m -s 5参数 -b 功能: 以字节&#xff08;bytes&#xff09;为单位显示内存使用情况。说…