Graph WaveNet代码入门详解

完整代码:GitHub - nnzhan/Graph-WaveNet: graph wavenet

DCRNN代码:GitHub - liyaguang/DCRNN: Implementation of Diffusion Convolutional Recurrent Neural Network in Tensorflow(需要从DCRNN项目代码中获得数据)

运行配置: Colab T4 GPU


运行示例(colab)

import os
path= '/content/drive/MyDrive/Graph-WaveNet-master'
os.chdir(path)
os.listdir(path)

generate data:

!python generate_training_data.py --output_dir=data/METR-LA --traffic_df_filename=metr-la.h5

train command

!python /content/drive/MyDrive/Graph-WaveNet-master/train.py --adjdata '/content/drive/MyDrive/DCRNN-master/data/sensor_graph/adj_mx.pkl' --device 'cuda:0'  

实际操作中,如果直接运行上述代码会报错‘Expected 2D (unbatched) or 3D (batched) input to conv1d, but got input of size: [64, 32, 207, 13]’

解决方案:将配置改成 python3.6,torch1.10.2

具体操作:参考用colab跑项目代码但是需要的环境是python3.6时,需要配置python3.6的环境_colab怎么配环境-CSDN博客

%%bash
MINICONDA_INSTALLER_SCRIPT=Miniconda3-4.5.4-Linux-x86_64.sh
MINICONDA_PREFIX=/usr/local
wget https://repo.continuum.io/miniconda/$MINICONDA_INSTALLER_SCRIPT
chmod +x $MINICONDA_INSTALLER_SCRIPT
./$MINICONDA_INSTALLER_SCRIPT -b -f -p $MINICONDA_PREFIX
!which conda # 返回/usr/local/bin/conda
!conda --version # 返回4.5.4
!which python # 依旧是/usr/local/bin/python
!python --version # 返回 Python 3.6.5 :: Anaconda, Inc.
%%bash
conda install --channel defaults conda python=3.6 --yes
conda update --channel defaults --all --yes
!conda --version # now returns 4.10.3
!python --version # now returns Python 3.6.13 :: Anaconda, Inc.
import sys
sys.path
_ = (sys.path.append("/usr/local/lib/python3.6/site-packages"))
_ = (sys.path.append("/usr/local/lib/python3.6/dist-packages"))
!sudo apt-get install python3-pip
!python -m pip install --upgrade pip
!pip install -q ipykernel numpy pandas torch argparse matplotlib time sys seaborn
!pip install -q tables scipy matplotlib ipykernel
!pip install torch==1.10.2

再运行
 

!python /content/drive/MyDrive/Graph-WaveNet-master/train.py --adjdata '/content/drive/MyDrive/DCRNN-master/data/sensor_graph/adj_mx.pkl' --device 'cuda:0'  

就可以啦~

主函数 train.py

命令行获取运行参数

parser = argparse.ArgumentParser()
parser.add_argument('--device',type=str,default='cuda:3',help='')
parser.add_argument('--data',type=str,default='data/METR-LA',help='data path')
parser.add_argument('--adjdata',type=str,default='data/sensor_graph/adj_mx.pkl',help='adj data path')
parser.add_argument('--adjtype',type=str,default='doubletransition',help='adj type')
parser.add_argument('--gcn_bool',action='store_true',help='whether to add graph convolution layer')
parser.add_argument('--aptonly',action='store_true',help='whether only adaptive adj')
parser.add_argument('--addaptadj',action='store_true',help='whether add adaptive adj')
parser.add_argument('--randomadj',action='store_true',help='whether random initialize adaptive adj')
parser.add_argument('--seq_length',type=int,default=12,help='')
parser.add_argument('--nhid',type=int,default=32,help='')
parser.add_argument('--in_dim',type=int,default=2,help='inputs dimension')
parser.add_argument('--num_nodes',type=int,default=207,help='number of nodes')
parser.add_argument('--batch_size',type=int,default=64,help='batch size')
parser.add_argument('--learning_rate',type=float,default=0.001,help='learning rate')
parser.add_argument('--dropout',type=float,default=0.3,help='dropout rate')
parser.add_argument('--weight_decay',type=float,default=0.0001,help='weight decay rate')
parser.add_argument('--epochs',type=int,default=100,help='')
parser.add_argument('--print_every',type=int,default=50,help='')
#parser.add_argument('--seed',type=int,default=99,help='random seed')
parser.add_argument('--save',type=str,default='./garage/metr',help='save path')
parser.add_argument('--expid',type=int,default=1,help='experiment id')

args = parser.parse_args()

这一部分的解析可以参考STGCN 代码解读(Pytorch)-CSDN博客

加载图信息

sensor_ids, sensor_id_to_ind, adj_mx = util.load_adj(args.adjdata,args.adjtype)

其中util.load_adj()主要引用utils文件中两个函数(除了Laplacian矩阵定义计算的几个函数之外):

def load_pickle(pickle_file):
    try:
        with open(pickle_file, 'rb') as f:
            pickle_data = pickle.load(f)
    except UnicodeDecodeError as e:
        with open(pickle_file, 'rb') as f:
            pickle_data = pickle.load(f, encoding='latin1')
    except Exception as e:
        print('Unable to load data ', pickle_file, ':', e)
        raise
    return pickle_data

def load_adj(pkl_filename, adjtype):
    sensor_ids, sensor_id_to_ind, adj_mx = load_pickle(pkl_filename)
    if adjtype == "scalap":
        adj = [calculate_scaled_laplacian(adj_mx)]
    elif adjtype == "normlap":
        adj = [calculate_normalized_laplacian(adj_mx).astype(np.float32).todense()]
    elif adjtype == "symnadj":
        adj = [sym_adj(adj_mx)]
    elif adjtype == "transition":
        adj = [asym_adj(adj_mx)]
    elif adjtype == "doubletransition":
        adj = [asym_adj(adj_mx), asym_adj(np.transpose(adj_mx))]
    elif adjtype == "identity":
        adj = [np.diag(np.ones(adj_mx.shape[0])).astype(np.float32)]
    else:
        error = 0
        assert error, "adj type not defined"
    return sensor_ids, sensor_id_to_ind, adj

load_pickle干了一件事:解析pkl文件

什么是pkl文件?

.pkl文件通常是使用Python中的pickle模块保存的二进制文件,它用于序列化(将对象转换为字节流)和反序列化(将字节流还原为对象)Python对象。这允许将Python对象保存到文件中以便后续使用,或者从文件中加载已保存的对象。

当使用pickle.load(f)加载一个.pkl文件时,它会返回文件f中存储的Python对象。例如,如果将一个列表对象序列化并保存到.pkl文件中,然后使用pickle.load(f)加载该文件,它将返回原始的列表对象。这使得在不丢失对象的结构或数据的情况下将对象保存到磁盘并重新加载它们成为可能。

将Python对象保存为.pkl文件相比于其他形式的文件保存有一些明显的好处:

  1. 完整性和结构保持:使用.pkl文件保存对象会保持对象的完整结构,包括嵌套对象、自定义类、函数等。这使得您可以轻松地保存和加载复杂的数据结构,而无需手动编写解析和序列化代码。

  2. 跨平台兼容性:.pkl文件是Python的标准二进制序列化格式,因此它在不同平台和Python版本之间具有很好的兼容性。您可以在不同计算环境中轻松地加载.pkl文件,而无需担心不同系统的字节顺序或其他兼容性问题。

  3. 快速和高效:pickle是一个非常高效的序列化方法,通常比使用文本格式(如JSON或XML)更快。这对于大型数据结构或需要频繁读写的数据非常有用。

  4. 支持自定义对象:您可以使用pickle保存自定义的Python对象,包括用户定义的类实例,而不需要额外的转换工作。这为保存包含用户自定义类的复杂数据结构提供了方便。

.pkl可以通过下载GitHub - liyaguang/DCRNN: Implementation of Diffusion Convolutional Recurrent Neural Network in Tensorflow

文件中data -> sensor_graph -> adj_mx.pkl获得

划分数据

dataloader = util.load_dataset(args.data, args.batch_size, args.batch_size, args.batch_size)

其函数定义在utils文件中load_dataset()函数给出

def load_dataset(dataset_dir, batch_size, valid_batch_size= None, test_batch_size=None):
    data = {}
    for category in ['train', 'val', 'test']:
        cat_data = np.load(os.path.join(dataset_dir, category + '.npz'))
        data['x_' + category] = cat_data['x']
        data['y_' + category] = cat_data['y']
    scaler = StandardScaler(mean=data['x_train'][..., 0].mean(), std=data['x_train'][..., 0].std())
    # Data format
    for category in ['train', 'val', 'test']:
        data['x_' + category][..., 0] = scaler.transform(data['x_' + category][..., 0])
    data['train_loader'] = DataLoader(data['x_train'], data['y_train'], batch_size)
    data['val_loader'] = DataLoader(data['x_val'], data['y_val'], valid_batch_size)
    data['test_loader'] = DataLoader(data['x_test'], data['y_test'], test_batch_size)
    data['scaler'] = scaler
    return data

接受的输入参数:数据集目录、批量大小以及验证集和测试集的批量大小。

  1. def load_dataset(dataset_dir, batch_size, valid_batch_size=None, test_batch_size=None):

    函数接受三个参数:
    • test_batch_size: 测试数据的批量大小,可选参数。
    • valid_batch_size: 验证数据的批量大小,可选参数。
    • batch_size: 训练数据的批量大小。
    • dataset_dir: 数据集目录的路径,其中包含训练、验证和测试数据文件。
  2. data = {}:  创建一个空的字典,用于存储加载的数据和数据加载器。

  3. for category in ['train', 'val', 'test']:

    遍历包含三个字符串的列表:'train'、'val' 和 'test'。这对应数据集中的三个部分:训练集、验证集和测试集。
  4. cat_data = np.load(os.path.join(dataset_dir, category + '.npz')):

    • 加载名为category的数据部分,npz 文件是NumPy的一种文件格式,通常包含多个数组。
    • os.path.join 用于构建完整的文件路径,dataset_dir 是数据集目录的路径,category + '.npz' 构建了特定部分的文件名。
    • 加载的数据存储在cat_data中。
  5. data['x_' + category] = cat_data['x']data['y_' + category] = cat_data['y']:

    将加载的数据分为特征('x')和目标('y')部分,并将它们存储在data字典中,使用键 'x_' + category'y_' + category 进行区分。这会创建六个不同的数组:x_trainy_trainx_valy_valx_testy_test
  6. scaler = StandardScaler(mean=data['x_train'][..., 0].mean(), std=data['x_train'][..., 0].std()):

    • 创建一个数据标准化(scaling)器,使用StandardScaler类。
    • 这个标准化器用于对数据进行标准化,以确保均值为0,标准差为1。它是根据训练集的特征('x_train'的第一个维度)的均值和标准差来初始化的。
  7. for category in ['train', 'val', 'test']:

    再次遍历数据集的三个部分。
  8. data['x_' + category][..., 0] = scaler.transform(data['x_' + category][..., 0]):

    对每个数据部分('x_train'、'x_val' 和 'x_test')的特征的第一个维度进行标准化,使用先前创建的标准化器 scaler
  9. data['train_loader'] = DataLoader(data['x_train'], data['y_train'], batch_size):

    创建一个 PyTorch 数据加载器(DataLoader),用于训练数据部分。它接受训练特征 data['x_train'] 和对应的标签 data['y_train'],以及批量大小 batch_size
  10. data['val_loader'] = DataLoader(data['x_val'], data['y_val'], valid_batch_size)data['test_loader'] = DataLoader(data['x_test'], data['y_test'], test_batch_size):

  • 创建验证数据和测试数据的数据加载器,使用相应的批量大小。
  1. data['scaler'] = scaler:
  • 将数据标准化器存储在 data 字典中,以便以后使用。
  1. return data:
  • 返回包含加载的数据和数据加载器的 data 字典。

这个函数的主要功能是加载数据,将数据分为训练、验证和测试部分,对数据进行标准化,然后创建用于训练、验证和测试的数据加载器,最后将这些数据和加载器打包到一个字典中返回。这是一个常见的数据预处理流程,特别是在深度学习任务中,以确保数据准备好用于训练和评估模型。

DataLoader类的定义:

class DataLoader(object):
    def __init__(self, xs, ys, batch_size, pad_with_last_sample=True):
        """
        :param xs:
        :param ys:
        :param batch_size:
        :param pad_with_last_sample: pad with the last sample to make number of samples divisible to batch_size.
        """
        self.batch_size = batch_size
        self.current_ind = 0
        if pad_with_last_sample:
            num_padding = (batch_size - (len(xs) % batch_size)) % batch_size
            x_padding = np.repeat(xs[-1:], num_padding, axis=0)
            y_padding = np.repeat(ys[-1:], num_padding, axis=0)
            xs = np.concatenate([xs, x_padding], axis=0)
            ys = np.concatenate([ys, y_padding], axis=0)
        self.size = len(xs)
        self.num_batch = int(self.size // self.batch_size)
        self.xs = xs
        self.ys = ys

    def shuffle(self):
        permutation = np.random.permutation(self.size)
        xs, ys = self.xs[permutation], self.ys[permutation]
        self.xs = xs
        self.ys = ys

    def get_iterator(self):
        self.current_ind = 0

        def _wrapper():
            while self.current_ind < self.num_batch:
                start_ind = self.batch_size * self.current_ind
                end_ind = min(self.size, self.batch_size * (self.current_ind + 1))
                x_i = self.xs[start_ind: end_ind, ...]
                y_i = self.ys[start_ind: end_ind, ...]
                yield (x_i, y_i)
                self.current_ind += 1

        return _wrapper()

 张量转移到device

device = torch.device(args.device)
supports = [torch.tensor(i).to(device) for i in adj_mx]

这段代码是为了将一个包含邻接矩阵的列表 adj_mx 中的每个张量转移到指定的 PyTorch 设备 device 上,以便与模型一起使用。

  1. device = torch.device(args.device): 这一行代码创建了一个 PyTorch 设备对象,以确定模型和张量运行的设备,根据 args.device 的值。args.device 可能是 'cuda:0'(表示使用第一块 GPU)或 'cpu'(表示使用 CPU)等。

  2. supports = [torch.tensor(i).to(device) for i in adj_mx]:

    • 这一行代码使用列表推导式对 adj_mx 列表中的每个张量进行处理,并将其移到指定的 device 上。
    • 对于列表 adj_mx 中的每个张量 itorch.tensor(i) 用于创建一个 PyTorch 张量,然后 to(device) 方法将这个张量移到指定的设备。
    • 最后,处理后的张量列表被赋值给变量 supports

随机初始化和是否有graph prior knowledge

parser.add_argument('--randomadj',action='store_true',help='whether random initialize adaptive adj')
parser.add_argument('--aptonly',action='store_true',help='whether only adaptive adj')

if args.randomadj:
        adjinit = None
    else:
        adjinit = supports[0]

if args.aptonly:
    supports = None

--randomadj:布尔类型的命令行参数,用于标志是否要进行随机初始化的操作。

--aptonly:这同样是一个布尔类型的命令行参数,通常用于标志是否只使用自适应邻接矩阵

由论文,对于是否拥有prior knowledge of graph对应两种空间卷积形式:

训练模型

engine = trainer(scaler, args.in_dim, args.seq_length, args.num_nodes, args.nhid, args.dropout,
                         args.learning_rate, args.weight_decay, device, supports, args.gcn_bool, args.addaptadj,
                         adjinit)


print("start training...",flush=True)

trainer是engine.py中定义的一个类:

class trainer():
    def __init__(self, scaler, in_dim, seq_length, num_nodes, nhid , dropout, lrate, wdecay, device, supports, gcn_bool, addaptadj, aptinit):
        self.model = gwnet(device, num_nodes, dropout, supports=supports, gcn_bool=gcn_bool, addaptadj=addaptadj, aptinit=aptinit, in_dim=in_dim, out_dim=seq_length, residual_channels=nhid, dilation_channels=nhid, skip_channels=nhid * 8, end_channels=nhid * 16)
        self.model.to(device)
        self.optimizer = optim.Adam(self.model.parameters(), lr=lrate, weight_decay=wdecay)
        self.loss = util.masked_mae
        self.scaler = scaler
        self.clip = 5

    def train(self, input, real_val):
        self.model.train()
        self.optimizer.zero_grad()
        input = nn.functional.pad(input,(1,0,0,0))
        output = self.model(input)
        output = output.transpose(1,3)
        #output = [batch_size,12,num_nodes,1]
        real = torch.unsqueeze(real_val,dim=1)
        predict = self.scaler.inverse_transform(output)

        loss = self.loss(predict, real, 0.0)
        loss.backward()
        if self.clip is not None:
            torch.nn.utils.clip_grad_norm_(self.model.parameters(), self.clip)
        self.optimizer.step()
        mape = util.masked_mape(predict,real,0.0).item()
        rmse = util.masked_rmse(predict,real,0.0).item()
        return loss.item(),mape,rmse

    def eval(self, input, real_val):
        self.model.eval()
        input = nn.functional.pad(input,(1,0,0,0))
        output = self.model(input)
        output = output.transpose(1,3)
        #output = [batch_size,12,num_nodes,1]
        real = torch.unsqueeze(real_val,dim=1)
        predict = self.scaler.inverse_transform(output)
        loss = self.loss(predict, real, 0.0)
        mape = util.masked_mape(predict,real,0.0).item()
        rmse = util.masked_rmse(predict,real,0.0).item()
        return loss.item(),mape,rmse

这段代码主要是为了训练和评估一个Graph WaveNet模型,其中enginetrainer 类的一个实例,trainer 类用于管理模型的训练和评估过程。下面是这段代码的详细解释:

首先,创建了一个 trainer 类的实例 engine,并且传递了多个参数给该实例:

  • scaler:数据标准化器,用于将数据标准化为均值为0、标准差为1的形式。
  • in_dim:输入数据的特征维度。
  • seq_length:时间序列数据的长度。
  • num_nodes:时间序列中的节点数目。
  • nhid:模型中的隐藏层维度。
  • dropout:模型中的丢弃率。
  • lrate:学习率。
  • wdecay:权重衰减。
  • device:模型运行的设备(CPU 或 GPU)。
  • supports:邻接矩阵,用于模型中的图卷积操作。
  • gcn_bool:一个布尔值,指示是否使用图卷积层。
  • addaptadj:一个布尔值,指示是否使用自适应邻接矩阵。
  • aptinit:邻接矩阵的初始化。

然后,以下代码段执行模型的训练和评估:

  1. self.model.train()self.optimizer.zero_grad(): 这两行代码将模型设置为训练模式,并将优化器的梯度清零。

  2. input = nn.functional.pad(input, (1, 0, 0, 0)): 这一行代码在输入数据的前面添加了一个维度,通常用于数据处理。

  3. output = self.model(input): 这行代码使用模型进行前向传播,生成模型的输出。

  4. self.loss(predict, real, 0.0): 这行代码计算模型的损失,其中 predict 是模型的预测值,real 是真实的目标值。

  5. loss.backward(): 这行代码执行反向传播,计算梯度。

  6. torch.nn.utils.clip_grad_norm_(self.model.parameters(), self.clip): 如果 self.clip 不为 None,则执行梯度裁剪,以避免梯度爆炸。

  7. self.optimizer.step(): 这行代码执行梯度下降步骤,更新模型的参数。

  8. util.masked_mape(predict, real, 0.0).item()util.masked_rmse(predict, real, 0.0).item(): 这两行代码计算模型的评估指标,包括 MAPE(平均绝对百分比误差)和 RMSE(均方根误差)。

整个 trainer 类用于管理模型的训练和评估过程,包括前向传播、损失计算、反向传播和梯度更新。它还包括了一些评估指标的计算,用于评估模型性能。这是一个典型的深度学习训练和评估过程的实现。

his_loss =[]
    val_time = []
    train_time = []
    for i in range(1,args.epochs+1):
        #if i % 10 == 0:
            #lr = max(0.000002,args.learning_rate * (0.1 ** (i // 10)))
            #for g in engine.optimizer.param_groups:
                #g['lr'] = lr
        train_loss = []
        train_mape = []
        train_rmse = []
        t1 = time.time()
        dataloader['train_loader'].shuffle()
        for iter, (x, y) in enumerate(dataloader['train_loader'].get_iterator()):
            trainx = torch.Tensor(x).to(device)
            trainx= trainx.transpose(1, 3)
            trainy = torch.Tensor(y).to(device)
            trainy = trainy.transpose(1, 3)
            metrics = engine.train(trainx, trainy[:,0,:,:])
            train_loss.append(metrics[0])
            train_mape.append(metrics[1])
            train_rmse.append(metrics[2])
            if iter % args.print_every == 0 :
                log = 'Iter: {:03d}, Train Loss: {:.4f}, Train MAPE: {:.4f}, Train RMSE: {:.4f}'
                print(log.format(iter, train_loss[-1], train_mape[-1], train_rmse[-1]),flush=True)
        t2 = time.time()
        train_time.append(t2-t1)
        #validation
        valid_loss = []
        valid_mape = []
        valid_rmse = []


        s1 = time.time()
        for iter, (x, y) in enumerate(dataloader['val_loader'].get_iterator()):
            testx = torch.Tensor(x).to(device)
            testx = testx.transpose(1, 3)
            testy = torch.Tensor(y).to(device)
            testy = testy.transpose(1, 3)
            metrics = engine.eval(testx, testy[:,0,:,:])
            valid_loss.append(metrics[0])
            valid_mape.append(metrics[1])
            valid_rmse.append(metrics[2])
        s2 = time.time()
        log = 'Epoch: {:03d}, Inference Time: {:.4f} secs'
        print(log.format(i,(s2-s1)))
        val_time.append(s2-s1)
        mtrain_loss = np.mean(train_loss)
        mtrain_mape = np.mean(train_mape)
        mtrain_rmse = np.mean(train_rmse)

        mvalid_loss = np.mean(valid_loss)
        mvalid_mape = np.mean(valid_mape)
        mvalid_rmse = np.mean(valid_rmse)
        his_loss.append(mvalid_loss)

        log = 'Epoch: {:03d}, Train Loss: {:.4f}, Train MAPE: {:.4f}, Train RMSE: {:.4f}, Valid Loss: {:.4f}, Valid MAPE: {:.4f}, Valid RMSE: {:.4f}, Training Time: {:.4f}/epoch'
        print(log.format(i, mtrain_loss, mtrain_mape, mtrain_rmse, mvalid_loss, mvalid_mape, mvalid_rmse, (t2 - t1)),flush=True)
        torch.save(engine.model.state_dict(), args.save+"_epoch_"+str(i)+"_"+str(round(mvalid_loss,2))+".pth")
    print("Average Training Time: {:.4f} secs/epoch".format(np.mean(train_time)))
    print("Average Inference Time: {:.4f} secs".format(np.mean(val_time)))

    #testing
    bestid = np.argmin(his_loss)
    engine.model.load_state_dict(torch.load(args.save+"_epoch_"+str(bestid+1)+"_"+str(round(his_loss[bestid],2))+".pth"))


    outputs = []
    realy = torch.Tensor(dataloader['y_test']).to(device)
    realy = realy.transpose(1,3)[:,0,:,:]

    for iter, (x, y) in enumerate(dataloader['test_loader'].get_iterator()):
        testx = torch.Tensor(x).to(device)
        testx = testx.transpose(1,3)
        with torch.no_grad():
            preds = engine.model(testx).transpose(1,3)
        outputs.append(preds.squeeze())

    yhat = torch.cat(outputs,dim=0)
    yhat = yhat[:realy.size(0),...]


    print("Training finished")
    print("The valid loss on best model is", str(round(his_loss[bestid],4)))


    amae = []
    amape = []
    armse = []
    for i in range(12):
        pred = scaler.inverse_transform(yhat[:,:,i])
        real = realy[:,:,i]
        metrics = util.metric(pred,real)
        log = 'Evaluate best model on test data for horizon {:d}, Test MAE: {:.4f}, Test MAPE: {:.4f}, Test RMSE: {:.4f}'
        print(log.format(i+1, metrics[0], metrics[1], metrics[2]))
        amae.append(metrics[0])
        amape.append(metrics[1])
        armse.append(metrics[2])

    log = 'On average over 12 horizons, Test MAE: {:.4f}, Test MAPE: {:.4f}, Test RMSE: {:.4f}'
    print(log.format(np.mean(amae),np.mean(amape),np.mean(armse)))
    torch.save(engine.model.state_dict(), args.save+"_exp"+str(args.expid)+"_best_"+str(round(his_loss[bestid],2))+".pth")
  1. his_loss, val_time, train_time:这些是用于存储历史损失、验证时间和训练时间的列表。

  2. 主循环 for i in range(1, args.epochs + 1):这个循环迭代训练模型多个周期(epochs)。

    • 内部包含以下步骤:
      • 训练集上的循环:
        • 每个周期开始时,初始化 train_losstrain_mapetrain_rmse 用于存储每个批次的损失和评估指标。
        • 数据加载器 dataloader['train_loader'] 被洗牌(shuffle),以确保随机访问训练数据。
        • 对于数据加载器中的每个批次,执行以下操作:
          • 将输入数据 x 和目标数据 y 转换为 PyTorch 张量,并移动到指定的设备 device 上。
          • 调用 engine.train 方法执行模型的训练,并获取损失和评估指标。
          • 将每个批次的损失、MAPE 和 RMSE 存储到相应的列表中。
        • 如果当前批次的迭代次数是 args.print_every 的倍数,将当前训练损失、MAPE 和 RMSE 打印到控制台。
      • 记录训练时间,并将其添加到 train_time 列表中。
      • 验证集上的循环:
        • 记录验证时间,并将其添加到 val_time 列表中。
        • 计算训练集和验证集的平均损失、MAPE 和 RMSE。
        • 将验证损失添加到 his_loss 列表中。
  3. 输出训练的性能指标和损失,同时保存训练模型的参数文件。

  4. 找到在验证集上表现最好的模型(损失最小的模型)。

  5. 使用最佳模型对测试数据进行推断。对于每个预测时刻,计算 MAE、MAPE 和 RMSE,并打印它们。

  6. 打印模型的性能指标:在每个预测时刻的平均 MAE、MAPE 和 RMSE。

  7. 最后,保存最佳模型的参数文件,并输出训练结束的信息。

  • 10
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 16
    评论
Graph-WaveNet是一种基于图神经网络的语音合成模型。它是Google DeepMind提出的一种创新性的语音合成方法。传统的语音合成模型如WaveNet使用的是基于序列的模型,即将语音合成视为一个逐个采样的过程,这样会导致计算效率低下和难以处理长时间的语音。而Graph-WaveNet则采用了基于图的模型,能够更好地解决这些问题。 Graph-WaveNet的核心思想是将语音信号转化为一个图形结构,其中节点表示音素或其它语音单位,边表示节点之间的依赖关系。这样可以将语音合成问题转化为在图上进行计算的问题。与序列模型相比,图模型能够充分利用语音信号中的局部和全局依赖关系,从而提高合成质量。 使用Graph-WaveNet进行语音合成的过程大致分为两步:图结构建模和声音合成。在图结构建模阶段,语音信号被分割成音素,并通过语音识别系统得到相应的标签。然后,根据音素序列构建一个有向无环图。在声音合成阶段,首先对图进行图卷积操作,以提取特征。然后,利用类似WaveNet的生成模型,根据输入的语音序列生成合成的声音信号。这样,Graph-WaveNet能够在保持高质量语音合成的同时,降低计算复杂度。 Graph-WaveNet的提出为语音合成领域带来了新的突破,使得合成的语音更加自然流畅。此外,Graph-WaveNet还可以扩展到多语种和多说话人的合成任务上,具有很好的拓展性和适应性。这些特点使得Graph-WaveNet成为目前语音合成领域的研究热点之一,并为未来更广泛的应用奠定了基础。
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值