目录
1.代码
1.1 代码阅读
1.2 代码分解
1.2.1 导入库
1.2.2 data = np.loadtxt('performance.txt')
1.2.3 mva = np.zeros((data.shape[0]), dtype=np.float)
1.2.4 mva[i] = data[i,1],mva[i] = alpha * data[i,1] + (1.0 - alpha) * mva[i-1]
1.2.5 plt.plot(data[:,0], data[:,1]) ,plt.plot(data[:,0], mva)
1.2.6 mvar[i] = alpha * data[i,2] + (1.0 - alpha) * mvar[i-1]
2.问题
1.代码
1.1 代码阅读
import numpy as np
import matplotlib.pyplot as plt
# 从文件中加载性能数据
data = np.loadtxt('performance.txt')
print(data.shape)
# 计算第一个图形中的移动平均值
alpha = 0.02
mva = np.zeros((data.shape[0]), dtype=np.float)
for i in range(data.shape[0]):
if i == 0:
mva[i] = data[i,1]
else:
mva[i] = alpha * data[i,1] + (1.0 - alpha) * mva[i-1]
# 绘制第一个图形:episode数量与时间步数的关系
plt.plot(data[:,0], data[:,1]) # 绘制原始数据
plt.plot(data[:,0], mva) # 绘制移动平均数据
plt.xlabel('episode #', fontsize=20) # 设置x轴标签
plt.ylabel('# of time steps', fontsize=20) # 设置y轴标签
plt.savefig('dqn1.png') # 保存图形到文件
# 清空绘图缓存
plt.clf()
plt.close()
# 计算第二个图形中的移动平均值
alpha = 0.02
mvar = np.zeros((data.shape[0]), dtype=np.float)
for i in range(data.shape[0]):
if i == 0:
mvar[i] = data[i,2]
else:
mvar[i] = alpha * data[i,2] + (1.0 - alpha) * mvar[i-1]
# 绘制第二个图形:时间步数与每个episode的奖励值的关系
plt.plot(data[:,3], data[:,2]) # 绘制原始数据
plt.plot(data[:,3], mvar) # 绘制移动平均数据
plt.xlabel('time step #', fontsize=20) # 设置x轴标签
plt.ylabel('episode reward', fontsize=20) # 设置y轴标签
plt.savefig('dqn2.png') # 保存图形到文件
这段代码通过加载 "performance.txt" 文件中的性能数据,计算并绘制了两个图形,分别表示 episode 数量与时间步数的关系以及时间步数与每个 episode 的奖励值的关系。移动平均值的计算采用了指数加权平均方法,使用了参数 alpha
控制平均值的权重。最后,通过 plt.savefig()
方法将绘制的图形保存到文件 "dqn1.png" 和 "dqn2.png" 中。
1.2 代码分解
1.2.1 导入库
import numpy as np
import matplotlib.pyplot as plt
这段代码导入了 NumPy 和 Matplotlib 库,这两个库在 Python 中用于进行数值计算和绘图。
- NumPy 是一个强大的数值计算库,提供了很多用于处理数组和矩阵的函数,包括数值计算、线性代数、傅里叶变换等。
- Matplotlib 是一个流行的数据可视化库,提供了绘制图形的功能,包括折线图、散点图、柱状图、饼图等,可以用于创建各种类型的图形。
这两个库经常一起使用,NumPy 用于处理数据,Matplotlib 用于将数据可视化。通过导入这两个库,你可以在 Python 程序中使用它们的函数和类来进行数值计算和绘图操作。
1.2.2 data = np.loadtxt('performance.txt')
data = np.loadtxt('performance.txt')
print(data.shape)
这行代码使用 NumPy 的 loadtxt()
函数从名为 performance.txt
的文本文件中加载数据,并将数据存储到名为 data
的 NumPy 数组中。然后,通过 data.shape
打印出 data
数组的形状信息。
performance.txt
文件中的数据可能包含了程序运行过程中的性能指标或者统计数据,例如每个 episode 的时步数、总奖励等。np.loadtxt()
函数可以读取文本文件中的数据,并将其转换为 NumPy 数组,便于后续的处理和分析。
print(data.shape)输出结果将显示 data
数组的形状,即 (m, n)
,其中 m
是数组的行数,n
是数组的列数。这可以帮助我们了解加载的数据的维度大小,以便后续的数据处理和绘图等操作。
注意:在运行这行代码之前,需要确保当前工作目录下存在名为 performance.txt
的文件,并且文件中包含了正确格式的数据。如果文件不存在或者文件格式不正确,np.loadtxt()
函数可能会抛出异常或者加载不成功。
1.2.3 mva = np.zeros((data.shape[0]), dtype=np.float)
alpha = 0.02
mva = np.zeros((data.shape[0]), dtype=np.float)
这段代码定义了一个变量 alpha
并赋值为 0.02,然后创建了一个长度为 data.shape[0]
的零数组 mva
,数据类型为浮点数 (dtype=np.float
)。这将用于存储移动平均值。
alpha
是用于计算移动平均值的权重,控制着历史值在计算中的权重大小。较小的 alpha
值会使得历史值在计算中的权重较大,从而导致移动平均值对历史数据更加敏感,变化较为迅速。而较大的 alpha
值会使得历史值在计算中的权重较小,从而导致移动平均值对历史数据变化较不敏感,变化较为平滑。在这段代码中,设置 alpha
为 0.02 可以使得移动平均值对历史数据变化较为敏感,变化较为迅速。
data.shape[0]
表示 data
数据的行数,即数据矩阵的第一维度的大小。在这段代码中,data
是一个二维数组,data.shape[0]
返回的是 data
的行数,即数据的数量。这个值会用于控制循环的次数,对每一行数据进行处理。
1.2.4 mva[i] = data[i,1],mva[i] = alpha * data[i,1] + (1.0 - alpha) * mva[i-1]
for i in range(data.shape[0]):
if i == 0:
mva[i] = data[i,1]
else:
mva[i] = alpha * data[i,1] + (1.0 - alpha) * mva[i-1]
这段代码实现了计算移动平均值(Moving Average)的操作。循环遍历 data
中的每一行数据,其中 data.shape[0]
表示数据的行数。在每一次循环中,使用当前行的第二列数据 data[i,1]
更新移动平均值 mva[i]
。
如果 i
等于 0,说明是第一行数据,此时直接将当前行的第二列数据赋值给 mva[i]
,作为初始值。
如果 i
大于 0,说明不是第一行数据,此时使用指数加权平均(Exponential Weighted Average,EMA)的方法计算移动平均值。公式为:
mva[i] = alpha * data[i,1] + (1.0 - alpha) * mva[i-1]
alpha
是平滑系数,通常取一个小于1的值,用于控制移动平均的权重。data[i,1]
表示第 i
行数据的第二列值(索引为 1),即在 data
数据中的第二列数据。mva[i-1]
表示上一个位置(索引为 i-1
)的指数移动平均值。
根据指数移动平均的定义,当前位置的指数移动平均值(mva[i])等于当前位置的数据(data[i,1])乘以平滑系数 alpha
,再加上上一个位置的指数移动平均值(mva[i-1])乘以 (1.0 - alpha)
。这样可以实现对历史数据的加权平均,使得新数据在计算中占有较大的权重,而旧数据在计算中的权重逐渐减小。这样计算出的指数移动平均值具有较好的平滑效果,能够较好地反映数据的趋势变化
(1)问:这里的新数据是指什么,为什么在计算中占有较大的比重?
在这段代码中,新数据指的是 data
数据中的第二列值,即 data[i,1]
。在指数移动平均中,新数据占有较大的比重是因为乘以了平滑系数 alpha
。较小的 alpha
值会使得新数据在计算中占有较大的权重,而旧数据在计算中的权重逐渐减小。
具体地说,alpha
控制了新数据在指数移动平均中的权重,取值范围为 0 到 1 之间,通常取一个小于1的值。当 alpha
较小的时候,新数据的权重较大,而旧数据的权重较小。这样可以使得指数移动平均能够较快地反应最近的数据变化,对数据的瞬时波动更加敏感。而随着时间的推移,旧数据的权重逐渐减小,使得指数移动平均能够对数据的趋势变化更具平滑性。因此,新数据在计算中占有较大的比重,能够较好地反映数据的近期变化趋势。
(2)问:alpha较小,为何新数据权重较大?
在指数移动平均中,实际上是新数据乘以平滑系数 alpha
加上旧数据乘以剩余的权重 (1-alpha
)。当 alpha
较小时,新数据乘以 alpha
的结果会比较小,但是由于是累积计算,新数据会不断地被乘以 alpha
的结果,因此在指数移动平均中新数据的权重会逐渐增大。
更具体地说,当 alpha
接近 0 时,新数据的权重接近 0,而旧数据的权重接近 1,这是因为 (1-
alpha)^n
在 n
取较大值时会趋近于 0。因此,当 alpha
较小时,新数据在指数移动平均中的权重较大,能够较好地反映数据的近期变化趋势。
1.2.5 plt.plot(data[:,0], data[:,1]) ,plt.plot(data[:,0], mva)
plt.plot(data[:,0], data[:,1]) # 绘制原始数据
plt.plot(data[:,0], mva) # 绘制移动平均数据
plt.xlabel('episode #', fontsize=20) # 设置x轴标签
plt.ylabel('# of time steps', fontsize=20) # 设置y轴标签
plt.savefig('dqn1.png')
plt.clf()
plt.close()
这段代码用于绘制原始数据和指数移动平均数据的折线图,并保存为图片文件 "dqn1.png"。
plt.plot(data[:,0], data[:,1])
绘制原始数据的折线图,其中data[:,0]
是 x 轴数据,data[:,1]
是 y 轴数据。plt.plot(data[:,0], mva)
绘制指数移动平均数据的折线图,其中data[:,0]
是 x 轴数据,mva
是 y 轴数据,即通过指数移动平均计算得到的平均值。plt.xlabel('episode #', fontsize=20)
设置 x 轴标签为 "episode #",字体大小为 20。plt.ylabel('# of time steps', fontsize=20)
设置 y 轴标签为 "# of time steps",字体大小为 20。plt.savefig('dqn1.png')
将绘制的图保存为 "dqn1.png" 图片文件。-
plt.clf()
是用于清除当前图像的函数,它会将当前图像中的所有绘图元素(如线条、点、文本等)清除,但不会关闭图像窗口。 -
plt.close()
是用于关闭当前图像的函数,它会关闭当前图像窗口,释放相关的系统资源。这样可以避免在后续的绘图中出现不必要的重叠或混淆。
plt.plot(data[:,0], data[:,1])
plt.plot(data[:,0], data[:,1])
是用于绘制原始数据的折线图。
data[:,0]
是data
数据中的第一列,表示 x 轴数据。data[:,1]
是data
数据中的第二列,表示 y 轴数据。
这段代码将使用 data[:,0]
作为 x 轴数据,data[:,1]
作为 y 轴数据,绘制折线图。具体绘制的内容和形式取决于 data[:,0]
和 data[:,1]
中的数据,可能表示了游戏的不同关卡或不同轮次的性能数据。
plt.plot(data[:,0], mva)
plt.plot(data[:,0], mva)
是用于绘制移动平均数据的折线图。
data[:,0]
是data
数据中的第一列,表示 x 轴数据。mva
是通过计算得到的移动平均数据,作为 y 轴数据。
这段代码将使用 data[:,0]
作为 x 轴数据,mva
作为 y 轴数据,绘制移动平均折线图。移动平均线是通过对原始数据进行平滑处理得到的,用于平滑数据的波动,以便更好地观察数据的趋势和变化。具体绘制的内容和形式取决于 data[:,0]
和 mva
中的数据,可能表示了游戏的不同关卡或不同轮次的移动平均性能数据。
plt.clf()
plt.close()
plt.clf()
是用于清除当前图像的函数,它会将当前图像中的所有绘图元素(如线条、点、文本等)清除,但不会关闭图像窗口。
plt.close()
是用于关闭当前图像的函数,它会关闭当前图像窗口,释放相关的系统资源。这样可以避免在后续的绘图中出现不必要的重叠或混淆。
这两个函数通常在生成多个图像时使用,可以帮助清除之前的图像并关闭图像窗口,以便绘制新的图像或在生成图像后释放系统资源。
1.2.6 mvar[i] = alpha * data[i,2] + (1.0 - alpha) * mvar[i-1]
alpha = 0.02
mvar = np.zeros((data.shape[0]), dtype=np.float)
for i in range(data.shape[0]):
if i == 0:
mvar[i] = data[i,2]
else:
mvar[i] = alpha * data[i,2] + (1.0 - alpha) * mvar[i-1]
# 绘制第二个图形:时间步数与每个episode的奖励值的关系
plt.plot(data[:,3], data[:,2]) # 绘制原始数据
plt.plot(data[:,3], mvar) # 绘制移动平均数据
plt.xlabel('time step #', fontsize=20) # 设置x轴标签
plt.ylabel('episode reward', fontsize=20) # 设置y轴标签
plt.savefig('dqn2.png') # 保存图形到文件
这段代码根据 data
中的第三列数据(每个 episode 的奖励值)计算了指数移动平均,并绘制了原始数据和移动平均数据的图形。
首先,创建了一个名为 mvar
的全零数组,用于存储每个时间步的移动平均值。
然后,使用循环遍历 data
中的每一行,计算移动平均值,并将结果存储在 mvar
数组中。
移动平均值的计算方式与之前的例子类似,使用了指数加权平均方法,其中 alpha
是平滑系数,控制了新数据和历史数据的权重。
最后,使用 plt.plot()
函数分别绘制了原始数据和移动平均数据的折线图,并使用 plt.xlabel()
和 plt.ylabel()
设置了 x 轴和 y 轴的标签。最后,使用 plt.savefig()
将图形保存到文件中。
2.问题
(1)问:为何再次执行程序从第一个回合开始写入performance.txt文件,但是在内容的末尾追加,而没有覆盖原先的数据,是为什么?
f = open("experiments/" + str(env.spec.id) + "/performance.txt", "a+")
这段代码中使用了 "a+" 模式来打开文件,表示以追加模式进行文件操作。
在 Python 中,文件操作模式通常由第二个参数来指定,其中 "a+" 表示以追加模式打开文件进行读写操作。具体解释如下:
'a'
:追加模式(append mode),允许在文件末尾进行写入操作,如果文件不存在则创建文件。'+'
:允许同时进行读写操作。'a+'
:追加模式 + 读写操作,既可以在文件末尾追加内容,也可以进行读取和写入操作。
因此,使用 "a+" 模式打开文件后,可以在文件末尾追加新的内容,而不会覆盖原先的数据。如果文件不存在,将会创建一个新的文件。在进行文件操作时,请注意谨慎操作,避免误删除或覆盖重要数据。