深度学习笔记(3)读懂代码和小试牛刀
一、如何读代码和读代码
1 下载的代码怎么看
首先找到我们的一个入口函数Train.py运行来debug
训练参数:
--name cifar10-100_500 # 训练名称
--dataset cifar10 # 数据集
--model_type ViT-B_16 # 模型类型
--pretrained_dir checkpoint/ViT-B_16.npz # 预训练模型路径
把上面这些训练参数复制一下
把脚本形参那里展开 复制到那里。
然后去找main函数,按ctrl点进去,去看main函数里面有什么,你会看到一些变量参数。
然后下拉到下面实际执行的第一行代码那里,打个断点。然后去点右上角debug,
下面这段代码是设备的初始化,判断你是否有GPU,如果没有就使用cpu,gpu是几张卡等等。然后就是训练的所有的数据和模型都传到这个设备里面去。
# Setup CUDA, GPU & distributed training
if args.local_rank == -1:#单机训练
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")#判断是否有GPU
args.n_gpu = torch.cuda.device_count()#GPU数量
else: #
torch.cuda.set_device(args.local_rank)#设置GPU
device = torch.device("cuda", args.local_rank)#
torch.distributed.init_process_group(backend='nccl',
timeout=timedelta(minutes=60))
args.n_gpu = 1
args.device = device
这是日志设置,很多代码都是差不多的,用的时候现查就好了。
# Setup logging
logging.basicConfig(format='%(asctime)s - %(levelname)s - %(name)s - %(message)s',
datefmt='%m/%d/%Y %H:%M:%S',
level=logging.INFO if args.local_rank in [-1, 0] else logging.WARN)
logger.warning("Process rank: %s, device: %s, n_gpu: %s, distributed training: %s, 16-bits training: %s" %
(args.local_rank, args.device, args.n_gpu, bool(args.local_rank != -1), args.fp16))
下面是设置种子,我们在训练模型的时候,很多时候咱们是做一个随机初始化但是现在遇到一个问题,我调参后参数变了,但是我初始化的权重也变了,那么测试后的结果,到底是因为你参数导致的,还是因为你的权重参数初始化好导致的会说不清楚,这时候设置个随机种子,但是每次随机结果都是固定的,所以我们要设置个随机种子。
# Set seed
set_seed(args)
你进去看随机种子的函数,以后用到的时候直接复制粘贴,因为所有项目都这么做的,不用太探究是因为什么。这几行代码很多年都不变了,就直接复制粘贴就可以了。
def set_seed(args):
random.seed(args.seed)
np.random.seed(args.seed)
torch.manual_seed(args.seed)
if args.n_gpu > 0:
torch.cuda.manual_seed_all(args.seed)
这段代码是传入参数,初始化参数,具体参数是什么呢?
# Model & Tokenizer Setup
args, model = setup(args)
点下看到的如下图:
可以在调试模式下按去看定义这函数的代码:
def setup(args):
# Prepare model
config = CONFIGS[args.model_type]
num_classes = 10 if args.dataset == "cifar10" else 100
model = VisionTransformer(config, args.img_size, zero_head=True, num_classes=num_classes)
model.load_from(np.load(args.pretrained_dir))
model.to(args.device)
num_params = count_parameters(model)
logger.info("{}".format(config))
logger.info("Training parameters %s", args)
logger.info("Total Parameter: \t%2.1fM" % num_params)
print(num_params)
return args, model
model = VisionTransformer(config, args.img_size, zero_head=True, num_classes=num_classes)这行代码比较重要 点进去看构造函数怎么做的。
在看源码的时候,先找类下面的forward打上断点。 类似这种
比如下面这段代码
class VisionTransformer(nn.Module):
def __init__(self, config, img_size=224, num_classes=21843, zero_head=False, vis=False):
super(VisionTransformer, self).__init__()
self.num_classes = num_classes#分类个数
self.zero_head = zero_head
self.classifier = config.classifier
self.transformer = Transformer(config, img_size, vis)#Transformer初始化
self.head = Linear(config.hidden_size, num_classes)
def forward(self, x, labels=None):
x, attn_weights = self.transformer(x)
print(x.shape)
logits = self.head(x[:, 0])
print(logits.shape)
if labels is not None:
loss_fct = CrossEntropyLoss()
loss = loss_fct(logits.view(-1, self.num_classes), labels.view(-1))
return loss
else:
return logits, attn_weights
我会看到类的名字是VisionTransformer,然后再看我的输入是啥,输入有一个x,把鼠标放x,会看它的一个维度。
可以看到维度是16是bach大小,3是rgb,224是h和w,H:Height,高度,表示图像垂直维度的像素数 W:Width,宽度,表示图像水平维度的像素数
再比如这段代码
class Transformer(nn.Module):
def __init__(self, config, img_size, vis):
super(Transformer, self).__init__()
self.embeddings = Embeddings(config, img_size=img_size)
self.encoder = Encoder(config, vis)
def forward(self, input_ids):
embedding_output = self.embeddings(input_ids)
encoded, attn_weights = self.encoder(embedding_output)
return encoded, attn_weights
点进input_ids
embedding操作,就是卷积输入是3,意思就是输入是个彩色图,输出每个位置得到 768 位向量。把一张输入图像展开展开成一个序列。
二、时间序列
1.一些基本概念
1.傅里叶变换 目的:将时域(即时间域)上的信号转变为频域(即频率域)上的信号,随着域的不同,对同一个事物的了解角度也就随之改变,因此在时域中某些不好处理的地方,在频域就可以较为简单的处理。
举个声音的例子
假如是一个声源始终发出440hz的A音,那么它的波形是这样的。每秒440次震荡,D音的结构是相同的,只是每秒节拍数变少了。那两个声音同时发出时,压强是怎么样的呢
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/3fc2718d99b441b294b89318e26f7b3b.png
重叠时,产生了很高的气压,而其它时刻又会互相抵消 、
加入更多音调的时候就更复杂了。那么我们在听音乐时,把音乐分解成简单的音调显然更容易理解。
那么先从最简单的说起,单一音调的音频是这样的,假设它每秒只有三拍,我们能轻松画出图像。
那么我们把这个图像抽象到一个圆上,也就是圆心到图像上的距离等于这个时刻的图像高度,而上面的图像是三拍每秒,下面的图像是每秒转半圈。当然,我们也可以加快圆旋转的速度。
但是,当他们频率都一样的时候,就会出现这种情况,高处的点出现在圆右侧,低处的点出现在左侧。也就是说当频率不一样的时候,这个图像的质心会在圆心处,但是一样的时候 ,质心会在右侧
那么,也就是说,每过3秒钟,质心会有一次飞跃的变化
同样的,频率为两秒的波形是这样的
然而你把这两个频率加在一起就变成这样的
2.timesnet (时间序列通用模型)
transformer等只针对原始数据,只针对原始序列去做的,原始序列当中给你提供信息毕竟是有限的,原来你的任务当中只能这一原始序列去做,那可能还是有些局限的。我们要分析这个时间序列,从一个新的维度,新的维度是什么呢?叫做多周期。任务里边可能肯定会有很多周期在发生不同的变化,每个周期要做自己的事,你就当做每个因素在做自己的一个事儿。然后他说这些个周期会有重叠,而且会相互影响的。对于每一个周期来说,我们发现了在每个周期的变化当中,不光是受它周围的点影响的,还受它相邻的周期的一个影响。这篇论文构建了一个周期内的一个变化,一个周期间的一个变化两种序列,但是1D 的时间序列很难去捕捉这两种类型的信息。,因此把研究对象转换到 2D 的空间当中,这是这篇论文的重点。
其实就是构建一个表,每一行代表不同周期同相位的过程。 每一列代表同周期内的一个变换。实现通过傅里叶变换实现。
注意时间序列上每一个点,它并不是一个值,每一个点它是一个向量。比如说每一个点我都知道这个人身高、年龄、体重,第一个点我知道身高、年龄、体重,第二点、第三个点、第四个点都有这些个指标。
timesnet 就是一共有t个点,每个点有c个特征,原始的一个 1D时间序列就可以表示成 t 乘c的 一个矩阵,要想考虑我们周期性的变换,我们需要先得一个周期是多少,通过傅立叶变换来分析。
做好傅里叶变换后就得到一个2d的矩阵,然后进行一个填充,为啥要填充呢,因为有的周期除不开,一旦除不开就要加一个padding
具体代码
源码里是脚本怎么办?打开.sh脚本比如
把参数复制过来