周报(6.24-6.30)

周报(6.24-6.30)

本周工作:

  1. 探索了DL-FWI经典模型的InversionNet的训练工作
  2. 了解常见优化器,如SGD、Adam等的区别,形成博客一篇。
  3. 构建数据集,优化数据加载。

构建数据集

我使用了Open-FWI的FlatVel_A数据集,该数据集大小为43GB,共30k条数据。要想把这些数据全部读入内存可不是个简单的事情。在训练之前,我观察了师兄的代码,师兄在构建数据集时直接将所有数据全部读入内存。我在学习过程中使用的电脑无法支持我将所有数据全部读入内存,因此采用了缓存机制加快运行速度。这种设计下数据集随机读取的代价仍然非常高昂,因此在加载DataLoader时,需要关闭shuffle参数。

为了方便模型在PyTorch上的调用,我将数据集包装为OpenFWI类。为了使用DataLoader调用数据集,需要在类里实现__len____getitem__方法。我的第一版数据集类如下:

class OpenFWI(Dataset):
    def __init__(self):
        self.DataPath = "/path/to/data/"
        # 使用正则表达式提取数据文件的编号
        pattern = re.compile(r"seismic(\d*)")
        fileList = os.listdir(os.path.join(self.DataPath, "seismic"))
        self.DataList = []
        for i in fileList:
            dataFileName = re.search(pattern,i)[1]
            if dataFileName not in self.DataList:
                self.DataList.append(dataFileName)
        # 保存数据文件路径到DataList中
        self.DataList.sort()
        self.Offset = 500 # FlatVel_A 每个数据包含500条数据
        self.Length = len(self.DataList)*self.Offset
        # 缓存
        self.Cache = {}
        # 缓存队列,当缓存被占满时,释放最久读入数据
        self.CacheQueue = Queue()
        # 10文件缓存
        self.CacheLength = 10

    def __len__(self)->int:
        return self.Length

    def __getitem__(self, index:int)->Tensor:
        realIndex = int(index / self.Offset)
        realoffset = index % self.Offset
        dataset = self.Cache.get(realIndex, None)
        # 如果存在缓存
        if dataset:
            data = dataset["data"]
            label = dataset["label"]
        else:
            # 删除过多缓存
            if self.CacheQueue.qsize() >= self.CacheLength:
                deleteAim = self.CacheQueue.get()
                del self.Cache[deleteAim]
            # 加载数据
            data = np.load(os.path.join(self.DataPath, "seis%s.npy"%self.DataList[realIndex]))
            label = np.load(os.path.join(self.DataPath, "vel%s.npy"%self.DataList[realIndex]))
            # 更新缓存
            self.Cache.update({
                realIndex: {
                    "data":data,
                    "label":label
                }
            })
            self.CacheQueue.put(realIndex)
        data = Tensor(data[realoffset,:,:,:])
        label = Tensor(label[realoffset,:,:])
        return data,label

但实际运行后,我发现这样的数据仍有问题,虽然单文件包含的500条数据可以迅速的被读取,但一旦读取新的文件的数据时,读取过程仍然会阻塞训练。因此我设计了第二版数据集类。

class OpenFWI(Dataset):
    def __init__(self):
        self.DataPath = "/path/to/data/"
        pattern = re.compile(r"seismic(\d*)")
        fileList = os.listdir(os.path.join(self.DataPath, "seismic"))
        self.DataList = []
        for i in fileList:
            dataFileName = re.search(pattern,i)[1]
            if dataFileName not in self.DataList:
                self.DataList.append(dataFileName)
        self.DataList.sort()
        self.Offset = 500 # FlatVel_A 每个数据包含500条数据
        self.Length = len(self.DataList)*self.Offset
        # 3数据缓存
        self.CacheLength = 3
        # 缓存队列
        self.CacheQueue = Queue(self.CacheLength)
        # 当前读取文件缓存
        self.ThisCache = None
        # 当前读取文件的编号
        self.ThisIndex = -2
        # 线程开始加载缓存的编号
        self.ThreadIndex = 0
        # 缓存线程
        self.Thread = Thread(target=DataReadThreader, args=(self,))
        # 线程运行状态
        self.ThreadRunning = True
        
        
    def __len__(self)->int:
        return self.Length
    
    def pushData(self, data):
        self.CacheQueue.put(data)
        
    def __getitem__(self,index):
        realIndex = int(index / self.Offset)
        realoffset = index % self.Offset
        if realIndex == self.ThisIndex:
            # 当 想要获取的数据在 当前读取文件 里时,直接返回对应的数据
            return self.ThisCache[0][realoffset,:,:,:], self.ThisCache[1][realoffset,:,:,:]
        elif realIndex == self.ThisIndex+1:
            # 如果想要的是下一个文件的数据,就将下一个文件从缓存载入到 当前读取文件
            self.ThisIndex += 1
            self.ThisCache = self.CacheQueue.get()
            return self.ThisCache[0][realoffset,:,:,:], self.ThisCache[1][realoffset,:,:,:]
        else:
            # 当数据既不在当前读取文件里,也不在下一个缓存文件里,则清空缓存,重启线程,设置线程读取文件编号
            self.ThreadIndex = realIndex
            # 设置当前文件编号
            self.ThisIndex = realIndex
            # 重启上个线程
            if self.Thread.is_alive():
                self.ThreadRunning = False
                while not self.CacheQueue.empty():
                    self.CacheQueue.get()
                self.Thread.join()
                self.ThreadRunning = True
            self.CacheQueue = Queue(self.CacheLength)
            self.Thread = Thread(target=DataReadThreader, args=(self,))
            self.Thread.start()
            # 读入缓存,CacheQueue是队列,当队列为空时会阻塞,直到读入数据
            self.ThisCache = self.CacheQueue.get()
            return  self.ThisCache[0][realoffset,:,:,:], self.ThisCache[1][realoffset,:,:,:]

# 缓存线程
def DataReadThreader(dataset:OpenFWI_Iter):
    # 从设置的文件读取编号开始,依次读入所有数据
    for i in range(dataset.ThreadIndex, len(dataset.DataList)):
        if not dataset.ThreadRunning:
            return
        data = np.load(os.path.join(dataset.DataPath, "seismic", "seismic%s.npy"%dataset.DataList[i]))
        label = np.load(os.path.join(dataset.DataPath, "vmodel", "vmodel%s.npy"%dataset.DataList[i]))
        data = Tensor(data)
        label = Tensor(label)
        # 将数据压入缓存,CacheQueue是队列,当队列满时会阻塞,直到Dataset取出数据导致队列非满
        dataset.CacheQueue.put((data, label))

这样一来,在训练过程中,数据集始终加载了未来要训练的数据,当模型训练时就能直接取出数据,减少IO时间。

模型训练

在训练前期,同一epoch下loss上下波动,不同epoch间loss下降缓慢,在参考师兄的代码做调整后loss仍然下降缓慢。但在交流学习后我了解到这是正常现象,参数较多的模型确实需要更多轮次和数据进行训练,检查模型的输出也逐渐有了速度模型的雏形。因此接下来准备进行完整的训练,对比几个优化器、损失函数的效果,同时预学习FCNVMB网络。

出现问题:

  1. 本地设备内存较小,无法将所有数据读入内存。通过改进数据集函数的方式完成优化。
  2. 模型Loss下降缓慢,根据结果来看确实在进行学习

下一步工作:

  1. 继续学习地震反演原理,深度学习原理。
  2. 对比我自己复现的代码与师兄的成熟代码,找到不同点,探索成熟代码的优势。
  3. 使用成熟代码改进复现InversionNet,解决已有问题,对比成熟代码的结果。
  4. 学习优化器中设计的权重衰减。
  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值