DQN代码框架 后向反射通信领域中的应用
参考论文:2019_CIC_Backscatter-Assisted Computation Offloading for Energy Harvesting IoT Devices via Policy-based Deep Reinforcement Learning
Python基础语法
random.seed /uniform/randn/random/randint/randrange/choice/normal
random.seed()是用来生成随机数的函数。
random.seed(x)中的参数可以为任意数字.
她们的区别:
- random.random()也是一个生成随机数的函数,但是每次调用后生成的随机数都是不同的
- random.seed(x) 设置好参数(种子,即x)后每次调用后生成的结果都是一样的。
- random.randn(2, 3) 生成一个2行3列的随机数组。
- random.uniform(x, y)方法将随机生成一个浮点数,它在 [x,y)范围内。若为uniform()则是生成0~1的浮点数。
- random.randint(a,b)
返回a,b之间的整数,范围[a,b],注意:传入参数必须是整数,a一定要比b小。 - random.randrange([start=0], stop[, step=1])
返回前闭后开区间[start,stop)内的整数,可以设置step。只能传入整数。 - numpy.random.choice(a, size=None, replace=True, p=None)
1)从a(只要是ndarray都可以,但必须是一维的)中随机抽取数字,并组成指定大小(size)的数组
replace:True表示可以取相同数字,False表示不可以取相同数字
数组p:与数组a相对应,表示取数组a中每个元素的概率,默认为选取每个元素的概率相同。
2)并且此方法还能用于返回一个列表、元组或字符串中的随机项。
对列表使用random.choice()方法时,随机返回一个列表最外层的元素。
对字符串使用random.choice()方法时,随意返回字符串中的一个字符. - numpy.random.normal(loc=0.0, scale=1.0, size=None)
功能:从正态(高斯)分布中抽取随机样本。
loc:分布的均值(中心)
scale:分布的标准差(宽度)
size:整数或者整数组成的元组,可选参数 输出值的维度。如果给定的维度为(m, n, k),那么就从分布中抽取m * n * k个样本。如果size为None(默认值)并且loc和scale均为标量,那么就会返回一个值。否则会返回np.broadcast(loc, scale).size个值.
out:从含参的正态分布中抽取的随机样本
np.hstack、np.vstack
- np.hstack()
按水平方向(列顺序)堆叠数组构成一个新的数组。堆叠的数组需要具有相同的维度。
例:[1,2]和[2,3],hstack后就是[1,2,2,3].
[s],[a],[r],[s_],hstack为[a,s,r,s_]
- np.vstack()
按垂直方向(行顺序)堆叠数组构成一个新的数组。堆叠的数组需要具有相同的维度。
Torch函数学习
torch.unsqueeze(x, dim)
unsqueeze(x, dim) x为tensor形式 dim为维度,函数含义为在对应维度(dim)位置上添加1个维度。
torch.unsqueeze(input, dim, out=None)
- 作用:扩展维度
返回一个新的张量,对输入的既定位置插入维度 1
- 注意 返回张量与输入张量共享内存,所以改变其中一个的内容会改变另一个。
如果dim为负,则将会被转化dim+input.dim()+1
-
参数:
tensor (Tensor) – 输入张量
dim (int) – 插入维度的索引
out (Tensor, optional) – 结果张量 -
unsqueeze_和 unsqueeze 的区别
unsqueeze_ 和 unsqueeze 实现一样的功能,区别在于 unsqueeze_ 是 in_place 操作,即 unsqueeze 不会对使用 unsqueeze 的 tensor 进行改变,想要获取 unsqueeze 后的值必须赋予个新值, unsqueeze_ 则会对自己改变。
squeeze(x,dim)
-
**作用:降维**
torch.squeeze(input, dim=None, out=None)
将输入张量形状中的1 去除并返回。 如果输入是形如(A×1×B×1×C×1×D),那么输出形状就为: (A×B×C×D)
当给定dim时,那么挤压操作只在给定维度上。如果在给定维度上shape为1 则会被挤压(去掉)否则不会。
- 注意: 返回张量与输入张量共享内存,所以改变其中一个的内容会改变另一个。
- 参数:
input (Tensor) – 输入张量
dim (int, optional) – 如果给定,则input只会在给定维度挤压
out (Tensor, optional) – 输出张量
torch.FloatTensor(x)
torch.FloatTensor()的作用就是把给定的list或者numpy转换成浮点数类型的tensor。
a.gather(dim,b)
gather参考链接
gather的作用是根据索引查找,然后讲查找结果以张量矩阵的形式返回。
最后的输出都可以看作是对a的查询,即元素都是a中的元素,查询索引都存在b中。输出大小与b一致。
找一张网图来描述,这里的index对应b,src对应a,格子里的数值都减1,左图对应dim=0,右图对应dim=1。
搭建DQN网络
存储记忆函数store_transition——class DQN
主要为对hstack横向堆叠函数,memory[index,;] = transition 行替换的学习。
def store_transition(self, s, a, r, s_): #存储记忆
transition = np.hstack((s, a, r, s_))
# 在水平方向上平铺拼接数组,**将传入的MOP元素横向堆叠进trans**
# replace the old memory with new memory
index = self.memory_counter % MEMORY_CAPACITY
#没到容量上限值为记忆数量本身,到了容量上线值为1,超过上限值从1开始出发又一次累积
self.memory[index, :] = transition
# 赋值/替换memory数组中**第index行的数据为当前transition**,
self.memory_counter += 1
# memory计数器 + 1
学习函数learn(self)——class DQN
分为两个部分,第一 个部分对目标网络的更新,较简单,每固定次数更新一次目标网络的参数。
第二部分是抽取batch个transitions,和gather函数。
# sample batch transitions
sample_index = np.random.choice(MEMORY_CAPACITY, BATCH_SIZE)
# 从0-1000中随机抽取BATCH_SIZE个值,sample_index为1行BATCH_SIZE列数组,值域为[0,1000)
b_memory = self.memory[sample_index, :]
# b_memory保存随机抽取的记录,取出memory中抽中的行,就是那些trans。
b_s = torch.FloatTensor(b_memory[:, :N_STATES])
# b_memory是[[s,a,r,s_],[...]],1*minibatch*4维度,第一维度的所有,第二维度的3列,对应状态s。
b_a = torch.LongTensor(b_memory[:, N_STATES:N_STATES+1].astype(int))
#第一维度所有,第二维度的第四列 #astype(int)) 就是转化为整形
b_r = torch.FloatTensor(b_memory[:, N_STATES+1:N_STATES+2])
b_s_ = torch.FloatTensor(b_memory[:, -N_STATES:])
q_eval = self.eval_net(b_s).gather(1, b_a) # shape (batch, 1) ,计算q_eval值
要知道[:,:]的含义首先要知道元组的维度。b_memory是存放抽取的trasitions的,它的维度是((s,a,r,s_),(…),(…),…),即是1batch_size4(不一定是4,这篇代码就是3+1+1+3).
[:,:]切片
-
[:j] 或者 [:i]:这是切片操作,在下标 i 或者 j 之前的元素都保留,适用于Python中的list(也就是数组),也适用于numpy科学结构(array等)。
-
关于[:,j] 或者 [:,i] :这也是切片操作,不同的是:保留第一个维度所有元素,第二维度元素保留到j;只适用numpy的科学数据结构。
-
定义一:在list里面,只存在元素,不存在元素中的元素;list里元素就是最小的成分,不可以再切片。
定义二:在array中(numpy的数据都可以)最后一个维度的数据才可以叫做元素,同样元素不可切分。
结合例子解释定义一:就算是二维、三维的list,元素就是axis=0的数据,例如上面的数据x=[[1,2,3,4],[2,3,4,5],[5,6,7,8]],元素就是[1,2,3,4]或者[2,3,4,5]或者[5,6,7,8],而不是更里面的数字1等等;在二维列表中写下x[:],但是不可以写x[:,:],根据定义一,第二个切片操作是不合法的。 -
关于[::]操作(高阶用法,可看可不看,一般出现在矩阵数据替换运算,例如NLP里的位置编码)。在list中可以用在元素层面,在numpy的数学数据中可以用在任何层面。
使用方法[start: end : step ],也就是[ 起始下标 : 终止下标 : 间隔距离 ]
切片范围是: start <= x < end,注意:一个小于等于,一个小于。
pickle.dump、pickle.load、open()
pickle模块实现了基本的数据序列和反序列化。通过pickle模块的序列化操作我们能够将程序中运行的对象信息保存到文件中去,永久存储;通过pickle模块的反序列化操作,我们能够从文件中创建上一次程序保存的对象。
- pickle.dump(obj, file, [,protocol])
功能:将obj对象序列化存入已经打开的file中。
obj:想要序列化的obj对象。
file:文件名称。file必须有write()接口, file可以是一个以’w’方式打开的文件或者一个StringIO对象或者其他任何实现write()接口的对象。如果protocol>=1,文件对象需要是二进制模式打开的。
protocol:序列化使用的协议。如果该项省略,则默认为0。如果为负值或HIGHEST_PROTOCOL,则使用最高的协议版本。
0:ASCII协议,所序列化的对象使用可打印的ASCII码表示;
1:老式的二进制协议;
2:2.3版本引入的新二进制协议,较以前的更高效。其中协议0和1兼容老版本的python。 - pickle.load(file)
功能:将file中的对象序列化读出。
file:文件名称。
open(name[, mode[, buffering]])
参考链接
open() 函数用于打开一个文件,创建一个 file 对象,相关的方法才可以调用它进行读写。
· name:要创建或打开文件的文件名称,该名称要用引号括起来。
· mode:mode 决定了打开文件的模式:只读,写入,追加等。默认文件访问模式为只读r。
· buffering:如果 buffering 的值被设为 0,就不会有寄存。如果 buffering 的值取 1,访问文件时会寄存行。如果将 buffering 的值设为大于 1 的整数,表明了这就是的寄存区的缓冲大小。如果取负值,寄存区的缓冲大小则为系统默认。
模式 | 描述 |
---|---|
r | 只读模式打开文件,读文件内容的指针会放在文件的开头。 |
rb | 以二进制格式、采用只读模式打开文件,读文件内容的指针位于文件的开头,一般用于非文本文件,如图片文件、音频文件等。 |
r+ | 打开文件后,既可以从头读取文件内容,也可以从开头向文件中写入新的内容,写入的新内容会覆盖文件中等长度的原有内容。 |
rb+ | 以二进制格式、采用读写模式打开文件,读写文件的指针会放在文件的开头,通常针对非文本文件(如音频文件)。 |
w | 以只写模式打开文件,若该文件存在,打开时会清空文件中原有的内容。 |
wb | 以二进制格式、只写模式打开文件,一般用于非文本文件(如音频文件) |
w+ | 打开文件后,会对原有内容进行清空,并对该文件有读写权限 |
wb+ | 以二进制格式、读写模式打开文件,一般用于非文本文件 |
a | 以追加模式打开一个文件,对文件只有写入权限,如果文件已经存在,文件指针将放在文件的末尾(即新写入内容会位于已有内容之后);反之,则会创建新文件。 |
ab | 以二进制格式打开文件,并采用追加模式,对文件只有写权限。如果该文件已存在,文件指针位于文件末尾(新写入文件会位于已有内容之后);反之,则创建新文件。 |
a+ | 以读写模式打开文件;如果文件存在,文件指针放在文件的末尾(新写入文件会位于已有内容之后);反之,则创建新文件。 |
ab+ | 以二进制模式打开文件,并采用追加模式,对文件具有读写权限,如果文件存在,则文件指针位于文件的末尾(新写入文件会位于已有内容之后);反之,则创建新文件。 |