回顾原型网络代码①episode数据加载

18 篇文章 0 订阅
12 篇文章 0 订阅

一般pytorch加载数据的固定格式是:

dataset = MyDataset()           # 第一步:构造Dataset对象
dataloader = DataLoader(dataset)# 第二步:通过DataLoader来构造迭代对象
num_epoches = 100
for epoch in range(num_epoches):# 第三步:逐步迭代数据
    for img, label in dataloader:
        # 训练代码

但是小样本有episode这个概念,所以需要额外用到一个sampler 。写篇文章记录下原型网络是怎么进行数据加载。
episode有分supprt集和query集,如果supprt集中有 N N N个类,每个类有 K K K个样本,我们就交做 N w a y − K s h o t N way -K shot NwayKshot。另外在query集每个类中有 Q Q Q个样本,注意这 Q Q Q个样本和supprt集中 K K K个样本没重复的样本。

DataLoader是怎么获得数据的

一文弄懂Pytorch的DataLoader, DataSet, Sampler之间的关系简单来说,就是如下图所示
在这里插入图片描述

当我们使用下面代码获取一个batch的数据的时候,sampler会先产生64个下标,然后Dataset会根据这64个下标获得数据,最后封装成一个tensor,返回给small_data

from torch.utils.data import DataLoader
train_dataloader = DataLoader(training_data, batch_size=64, shuffle=True)

for small_data in train_dataloader:
	print(data.shape)

原型代码中的dataloader、simpler和dataset分别对应于

ds = TransformDataset(ListDataset(class_names), transforms)
EpisodicBatchSampler
torch.utils.data.DataLoader(ds, batch_sampler=sampler, num_workers=0)

N w a y − K s h o t − Q q u e r y N way -K shot - Q query NwayKshotQqueryepisode

dataset

在原型代码中用omniglot数据集训练,在图片外面有两个文件夹,第一层是①种类,第二层是②编号。注意原来的omniglot数据集有区分images_background.zipimages_background.zip,代码作者分别解压之后,合并到了omniglot/data文件夹下,详细可以看官方代码download_omniglot.sh文件。
在这里插入图片描述
在官方代码中有对omniglot进行分割的txt文件,如下图所示。
在这里插入图片描述
可以在想要训练集、验证集和测试集时分别读取不同的txt文件完成。以train为例,train.txt的第一行为:

Angelic/character01/rot000

分别代表了①种类/②编号/旋转的角度。

line115首先读取train.txt的所有行,将它保存至class_names = [ ]中.

然后在119行

ds = TransformDataset(ListDataset(class_names), transforms)

先用torchnet.dataset.ListDatasetclass_names转换成ListDataset
查看官方文档教程_产生List形式数据,可以发现就是在获得ListDataset(class_names)中的任意一项数据时,都会对它、数据进行transforms变换,内容如下:

transforms = [partial(convert_dict, 'class'),
                      load_class_images,
                      partial(extract_episode, n_support, n_query)]

其中convert_dictload_class_imagesextract_episode均为代码作者自定义的函数。而partial关键字就是在调用函数的同时,有几个参数固定为所给定的值。以partial(convert_dict, 'class')为例,就等同于将convert_dict函数从

def convert_dict(k, v):
    return { k: v }

转变为

def convert_dict(v):
	# 此时已经不用传k的值了,因为 partial(convert_dict, 'class') 已经给k 赋值了'class'
    return { k: v }

如果有function(a,b,c,d,e,f,g),则

  • partial(convert_dict, 'value1') 就已经给参数a传了value1
  • partial(convert_dict, 'value1', 'value2') 就已经给参数a传了value1、参数b传了value2
  • partial(convert_dict, 'value1', 'value2' ,'value3') 就已经给参数a传了value1、参数b传了value2、参数c传了value3
  • 等等等等

Avesta/character12/rot090为例,我们来看transforms最终能取得什么效果。在原型代码中设置的episode 5 w a y − 5 s h o t − 15 q u e r y 5way - 5shot - 15query 5way5shot15query,为了区分两个5,我们在这以 10 w a y − 5 s h o t − 15 q u e r y 10way - 5shot - 15query 10way5shot15query进行举例。

transforms = [partial(convert_dict, 'class'),
              load_class_images,
              partial(extract_episode, 5, 15)]
  • 首先把Avesta/character12/rot090传进partial(convert_dict, 'class')
def convert_dict(k, v):
    return { k: v }

返回结果为 { 'class' :"Avesta/character12/rot090" }

  • 然后把 { 'class' :"Avesta/character12/rot090" }传进load_class_images
def load_class_images(d):  # { 'class' :"Avesta/character12/rot090" }
    if d['class'] not in OMNIGLOT_CACHE:
        alphabet, character, rot = d['class'].split('/')  
        # 值分别为 Avesta  character12  rot090
        
        image_dir = os.path.join(OMNIGLOT_DATA_DIR, 'data', alphabet, character)
        # OMNIGLOT_DATA_DIR是omniglot的根路径,此句就是为了拼接出Avesta/character12的路径

        class_images = sorted(glob.glob(os.path.join(image_dir, '*.png')))
        # 获得路径下以png结尾的文件路径,然后排序,这是一个列表

        if len(class_images) == 0:
            raise Exception("No images found for omniglot class {} at {}. Did you run download_omniglot.sh first?".format(d['class'], image_dir))

        image_ds = TransformDataset(ListDataset(class_images), # 这个同上文讲过的,不再赘述
                                    compose([partial(convert_dict, 'file_name'),
                                             partial(load_image_path, 'file_name', 'data'),
                                             partial(rotate_image, 'data', float(rot[3:])),
                                             partial(scale_image, 'data', 28, 28),
                                             partial(convert_tensor, 'data')]))

        loader = torch.utils.data.DataLoader(image_ds, batch_size=len(image_ds), shuffle=False)
        # 将全部数据封装成一个batch,作为一个episode中的一个类

        for sample in loader:
            OMNIGLOT_CACHE[d['class']] = sample['data']
            break # only need one sample because batch size equal to dataset length

    return { 'class': d['class'], 'data': OMNIGLOT_CACHE[d['class']] }

返回结果为

{ 'class' :"Avesta/character12/rot090" ,
'data':  size为(20,1,28,28)的一个tensor
}

注意(20,1,28,28)是固定的,因为一个文件夹下面只有20张28*28的黑白图片

  • 然后把上面的返回结果传进extract_episode,注意是 10 w a y − 5 s h o t − 15 q u e r y 10way - 5shot - 15query 10way5shot15query进行举例,所以每个类有5个作为support,15个作为query
def extract_episode(n_support, n_query, d):
    # data: N x C x H x W
    n_examples = d['data'].size(0)  # 20

    if n_query == -1:
        n_query = n_examples - n_support  

    example_inds = torch.randperm(n_examples)[:(n_support+n_query)]
    # 从20个样本中 选取5+15个
    
    support_inds = example_inds[:n_support]
    # 从中选5个作为support
    
    query_inds = example_inds[n_support:]
    # 剩下的20-5个作为query

	# 根据下标加载数据
    xs = d['data'][support_inds]
    xq = d['data'][query_inds]

    return {
        'class': d['class'],
        'xs': xs,
        'xq': xq
    }

返回结果为:

{
	'class': "Avesta/character12/rot090" ,
	'xs': size为(5,1,28,28)的一个tensor ,
	'xq': size为(15,1,28,28)的一个tensor
}

总而言之

line38的流程为:
在这里插入图片描述
line 107的transform的流程为
在这里插入图片描述

simpler

根据EpisodicBatchSampler的定义调用,可以根据需求生成类的下标

class EpisodicBatchSampler(object):
    def __init__(self, n_classes, n_way, n_episodes):
        self.n_classes = n_classes
        self.n_way = n_way
        self.n_episodes = n_episodes

    def __len__(self):
        return self.n_episodes

    def __iter__(self):
        for i in range(self.n_episodes):
            yield torch.randperm(self.n_classes)[:self.n_way]
sampler = EpisodicBatchSampler(len(ds), n_way, n_episodes)

此时len(ds)等于train.txt的行数,也就是4112,n_way等于10, n_episodes等于100,就是一个epoch里有100个episode,每个episode有10个类。从EpisodicBatchSampler的定义的定义,我们可以发现,官方代码认为字符和转换某个角度(90、180、270)后的字符,是不一样的。可以看做是一种数组增强吧🤡。
另外sampler的代码显示它返回的是list,有10个下标。

dataloader

然后dataloader根据它提供的10个下标,去dataset找对应下标的数据:

{
	'class': "Avesta/character12/rot090" ,
	'xs': size为(5,1,28,28)的一个tensor ,
	'xq': size为(15,1,28,28)的一个tensor
}

然后dataloader将10个数据合并成一个episode

line35使用了dataloader

for sample in tqdm(state['loader'], desc="Epoch {:d} train".format(state['epoch'] + 1)):

其中state['loader']定义在line39tqdm相当于有进度条的for循环
在这里插入图片描述

因此,功能上可以看作

for sample in tqdm(state['loader'], desc="Epoch {:d} train".format(state['epoch'] + 1)):
	...
# 等价于
for sample in train_loader:
	...

调试分析sample,可以看到和推断一致
在这里插入图片描述

  • 9
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
以下是使用深度 Q 网络(DQN)对八分类数据进行强化学习的 Python 代码示例: ```python import numpy as np import random from keras.models import Sequential from keras.layers import Dense from keras.optimizers import Adam class DQNAgent: def __init__(self, state_size, action_size): self.state_size = state_size self.action_size = action_size self.memory = [] self.gamma = 0.95 # discount rate self.epsilon = 1.0 # exploration rate self.epsilon_min = 0.01 self.epsilon_decay = 0.995 self.learning_rate = 0.001 self.model = self._build_model() def _build_model(self): # Neural Network for Deep Q Learning model = Sequential() model.add(Dense(24, input_dim=self.state_size, activation='relu')) model.add(Dense(24, activation='relu')) model.add(Dense(self.action_size, activation='linear')) model.compile(loss='mse', optimizer=Adam(lr=self.learning_rate)) return model def remember(self, state, action, reward, next_state, done): self.memory.append((state, action, reward, next_state, done)) def act(self, state): if np.random.rand() <= self.epsilon: return random.randrange(self.action_size) act_values = self.model.predict(state) return np.argmax(act_values[0]) def replay(self, batch_size): minibatch = random.sample(self.memory, batch_size) for state, action, reward, next_state, done in minibatch: target = reward if not done: target = (reward + self.gamma * np.amax(self.model.predict(next_state)[0])) target_f = self.model.predict(state) target_f[0][action] = target self.model.fit(state, target_f, epochs=1, verbose=0) if self.epsilon > self.epsilon_min: self.epsilon *= self.epsilon_decay def load(self, name): self.model.load_weights(name) def save(self, name): self.model.save_weights(name) # main function if __name__ == "__main__": # initialize gym environment and the agent state_size = 8 action_size = 8 agent = DQNAgent(state_size, action_size) # train DQN agent batch_size = 32 episodes = 1000 for e in range(episodes): state = np.random.rand(1, state_size) for time in range(500): action = agent.act(state) next_state = np.random.rand(1, state_size) reward = np.random.randint(0, 2) done = False agent.remember(state, action, reward, next_state, done) state = next_state if done: print("episode: {}/{}, score: {}, e: {:.2}" .format(e, episodes, time, agent.epsilon)) break if len(agent.memory) > batch_size: agent.replay(batch_size) # save trained model weights agent.save("model.h5") ``` 以上代码示例中,采用了 Keras 框架搭建了一个具有两个隐藏层的神经网络模型,并使用 Q-learning 算法进行训练。在训练过程中,每个时间步,根据当前状态选择一个动作并执行,然后通过环境返回一个奖励信号以及下一个状态。将这些信息存储在记忆库中,每次从记忆库中随机抽取一批数据进行训练,直到达到一定的训练次数或准确率。最后保存训练好的模型权重,以备后续使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值