训练小tricks


工具

pytorch

内存相关

原地操作

运行一些操作可能会导致为新结果分配内存。 例如,如果我们用Y=X+Y,我们将取消引用Y指向的张量,而是指向新分配的内存处的张量。

before = id(Y)
Y = Y + X
id(Y) == before
False

首先,我们不想总是不必要地分配内存。在机器学习中,我们可能有数百兆的参数,并且在一秒内多次更新所有参数。 通常情况下,我们希望原地执行这些更新。 其次,如果我们不原地更新,其他引用仍然会指向旧的内存位置, 这样我们的某些代码可能会无意中引用旧的参数。

幸运的是,执行原地操作非常简单。 我们可以使用切片表示法将操作的结果分配给先前分配的数组,例如Y[:] = <expression>。 为了说明这一点,我们首先创建一个新的矩阵Z,其形状与另一个Y相同, 使用zeros_like来分配一个全0的块。

Z = torch.zeros_like(Y)
print('id(Z):', id(Z))
Z[:] = X + Y
print('id(Z):', id(Z))

输出:

id(Z): 139931132035296
id(Z): 139931132035296

如果在后续计算中没有重复使用X, 我们也可以使用X[:] = X + YX += Y来减少操作的内存开销

before = id(X)
X += Y
id(X) == before

输出:

True

EarlyStopping

# 定义 EarlyStopping Callback
class EarlyStopping:
    def __init__(self, patience=10, delta=0):
        self.patience = patience
        self.delta = delta
        self.best_score = None
        self.counter = 0
        self.early_stop = False

    def __call__(self, val_loss):
        score = -val_loss
        if self.best_score is None:
            self.best_score = score
        elif score < self.best_score + self.delta:
            self.counter += 1
            if self.counter >= self.patience:
                self.early_stop = True
        else:
            self.best_score = score
            self.counter = 0

linux 服务器守护线程——tmux

$HOME : echo $HOME = ‘/data/kangbaobin’【是Linux中的一个环境变量,表示用户初次登陆时的起始目录名】

Xshell断开连接后仍保持服务器程序执行

先安装tmux:

root用户安装仅需一行

sudo apt-get install tmux

非root用户

1、下载与解压

wget -c https://github.com/tmux/tmux/releases/download/3.0a/tmux-3.0a.tar.gz
wget -c https://github.com/libevent/libevent/releases/download/release-2.1.11-stable/libevent-2.1.11-stable.tar.gz
wget -c https://ftp.gnu.org/gnu/ncurses/ncurses-6.2.tar.gz

解压指令如下:

tar -xzvf tmux-3.0a.tar.gz
tar -xzvf libevent-2.1.11-stable.tar.gz
tar -xzvf ncurses-6.2.tar.gz

2、安装

libevent会安在 /data/kangbaobin/tmux/tmux_depend / lib

cd  libevent-2.1.11-stable
 
#       $HOME/data/kangbaobin/tmux/tmux_depend 是我的安装路径,大家可以修改
 
./configure --prefix=$HOME/data/kangbaobin/tmux/tmux_depend --disable-shared
make && make install

ncurses会安在 /data/kangbaobin/tmux/tmux_depend / include

cd  ncurses-6.2
./configure --prefix=$HOME/data/kangbaobin/tmux/tmux_depend
make && make install

安装tmux

cd  tmux-3.0a
./configure CFLAGS="-I$HOME/data/kangbaobin/tmux/tmux_depend/include -I/$HOME/data/kangbaobin/tmux/tmux_depend/include/ncurses" LDFLAGS="-L/$HOME/data/kangbaobin/tmux/tmux_depend/lib -L/$HOME/data/kangbaobin/tmux/tmux_depend/include/ncurses -L/$HOME/data/kangbaobin/tmux/tmux_depend/include"
make
cp tmux  $HOME/data/kangbaobin/tmux/tmux_depend/bin

3、设置环境变量(此步骤建议手动添加到bashrc文件中)

export PATH=$HOME/data/kangbaobin/tmux/tmux_depend/bin:$PATH
source ~/.bashrc

tmux常用命令

1)新建会话,比如新创建一个会话以"ccc"命名
 
[root@Centos6 ~]# tmux new -s ccc
 
加上参数-d,表示在后台新建会话
 
root@bobo:~# tmux new -s shibo -d
 
root@bobo:~# tmux ls
 
shibo: 1 windows (created Tue Oct  2 19:22:32 2018) [135x35]
 
 
 
2)查看创建得所有会话
 
[root@Centos6 ~]# tmux ls
 
0: 1 windows (created Wed Aug 30 17:58:20 2017) [112x22](attached)    #这里的attached表示该会话是当前会话
 
aaa: 2 windows (created Wed Aug 30 16:54:33 2017) [112x22]
 
ccc: 1 windows (created Wed Aug 30 17:01:05 2017) [112x22]
 
   
 
3)登录一个已知会话。即从终端环境进入会话。
 
第一个参数a也可以写成attach。后面的aaa是会话名称。
 
[root@Centos6 ~]# tmux a -t aaa 
 
  
 
4)退出会话不是关闭:
 
登到某一个会话后,先按键ctrl+b启动快捷键,再按d,这样就会退出该会话,但不会关闭会话。
 
如果直接ctrl + d,就会在退出会话的通话也关闭了该会话!
 
   
 
5)关闭会话(销毁会话)
 
[root@Centos6 ~]# tmux ls
 
aaa: 2 windows (created Wed Aug 30 16:54:33 2017) [112x22]
 
bbb: 1 windows (created Wed Aug 30 19:02:09 2017) [112x22]
 
   
 
[root@Centos6 ~]# tmux kill-session -t bbb
 
   
 
[root@Centos6 ~]# tmux ls
 
aaa: 2 windows (created Wed Aug 30 16:54:33 2017) [112x22]
 
  
 
6)重命名会话
 
[root@Centos6 ~]# tmux ls  
 
wangshibo: 1 windows (created Sun Sep 30 10:17:00 2018) [136x29] (attached)
 
  
 
[root@Centos6 ~]# tmux rename -t wangshibo kevin
 
  
 
[root@Centos6 ~]# tmux ls
 
kevin: 1 windows (created Sun Sep 30 10:17:00 2018) [136x29] (attached)

Python

计时器

class Timer:  
    """记录多次运行时间"""
    def __init__(self):
        self.times = []
        self.start()

    def start(self):
        """启动计时器"""
        self.tik = time.time()

    def stop(self):
        """停止计时器并将时间记录在列表中"""
        self.times.append(time.time() - self.tik)
        return self.times[-1]

    def avg(self):
        """返回平均时间"""
        return sum(self.times) / len(self.times)

    def sum(self):
        """返回时间总和"""
        return sum(self.times)

    def cumsum(self):
        """返回累计时间"""
        return np.array(self.times).cumsum().tolist()

lambda函数

  • lambda函数是一种匿名函数,即没有名字的函数
  • 使用lambda保留字定义,函数名是返回结果
  • lambda函数的函数体只是一个表达式
  • lambda函数用于定义简单的、能够在一行内表示的函数
  • lambda表达式” : “后面,只能有一个表达式,def则可以有多个。
  • lambda一般用来定义简单的函数(单行函数),而def可以定义复杂的函数
g = lambda x:x*x+1
g(3)#10

封装日志类

以下是一个比较复杂的 Python 封装日志类的示例代码,其中包含了很多不同的功能和选项:

import logging
import os
import sys

class CustomLogger:

    def __init__(self, logger_name, log_level=logging.DEBUG, log_dir=None, log_filename=None, console_log=True, file_log=True):

        # create logger
        self.logger = logging.getLogger(logger_name)
        self.logger.setLevel(log_level)

        # set log format
        formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

        # add console handler
        if console_log:
            ch = logging.StreamHandler(sys.stdout)
            ch.setFormatter(formatter)
            self.logger.addHandler(ch)

        # add file handler
        if file_log:
            if not log_dir:
                log_dir = os.getcwd()
            if not log_filename:
                log_filename = logger_name + '.log'
            log_file_path = os.path.join(log_dir, log_filename)
            fh = logging.FileHandler(log_file_path)
            fh.setFormatter(formatter)
            self.logger.addHandler(fh)

    def debug(self, message):
        self.logger.debug(message)

    def info(self, message):
        self.logger.info(message)

    def warning(self, message):
        self.logger.warning(message)

    def error(self, message):
        self.logger.error(message)

    def critical(self, message):
        self.logger.critical(message)

在上面的代码中,CustomLogger 类接受以下参数:

  • logger_name:指定 logger 的名称。
  • log_level:指定 logger 的日志级别,默认为 logging.DEBUG
  • log_dir:指定日志文件存储的目录,默认为当前工作目录。
  • log_filename:指定日志文件的文件名,默认为 logger 的名称加上 .log 后缀。
  • console_log:如果为 True,将会向控制台输出日志。
  • file_log:如果为 True,将会将日志写入文件。

CustomLogger 类在初始化时会创建 logger 对象,并设置日志级别。然后,它会根据给定的参数决定是否添加控制台处理器和文件处理器。控制台处理器将日志输出到标准输出流,文件处理器将日志写入指定的文件。如果没有指定日志文件的存储目录和文件名,则会使用当前工作目录和 logger 名称加上 .log 后缀。

CustomLogger 类还定义了一些方法,包括 debug()info()warning()error()critical(),这些方法分别对应不同的日志级别,并将日志消息传递给 logger 对象进行处理。

使用该类时,您可以像下面这样实例化:

logger = CustomLogger(logger_name='my_logger', log_level=logging.INFO, log_dir='/path/to/logs', log_filename='my_app.log', console_log=True, file_log=True)

logger.info('Hello, world!')

这将创建一个名为 my_logger 的 logger 对象,日志级别为 logging.INFO,日志文件存储在 /path/to/logs/my_app.log,同时向控制台输出日志。然后,它将记录一条日志消息:“Hello, world

jupyter

如何在jupyter中可视化训练效果?

某些情况不方便弹窗把 env 界面实时渲染出来,所以我们通过下面两种方法环境的可视化,从而看到智能体的交互情况。

方法一:直接在jupyter中渲染

import gym
from IPython import display
import matplotlib
import matplotlib.pyplot as plt
%matplotlib inline

# Set up fake display; otherwise rendering will fail
import os
os.system("Xvfb :1 -screen 0 1024x768x24 &")
os.environ['DISPLAY'] = ':1'

env = gym.make('CartPole-v1')
env.seed(23)
env.reset()
img = plt.imshow(env.render(mode='rgb_array')) # only call this once
for _ in range(100):
    img.set_data(env.render(mode='rgb_array')) # just update the data
    display.display(plt.gcf())
    display.clear_output(wait=True)
    action = env.action_space.sample()
    env.step(action)

方法二:对环境进行录制,再播放视频

由于工具函数太长了,具体见github中的代码

下面录制视频和播放视频:

# 录制视频
record_video('CartPole-v1', model, video_length=500, prefix='ppo2-cartpole')
# 展示视频
show_videos('videos', prefix='ppo2')

网络结构

  1. 网络输出离散动作概率的时候,一般加一层softmax层,保证输出的离散动作概率和为1;输出连续动作概率的时候,一般加一层tanh层,因为tanh范围是[-1,1],我们可以根据实际需要,将输出进行缩放。

    image-20220911092035223

好玩的东西

NOTE:

\r后的字符串替换前面输出的字符串.

eg.print(“hello\rwork”)输出为work

下面的代码的功能是在训练中进度条式的显示输出结果,而不是一大长串字符输出。

import numpy as np
import time
for t in range(5):
    for i in range(100):
        print("\rStep {} Episode {},{} loss=({})".format(
            t, i + 1, 100, np.random.randn()), end="")
        time.sleep(0.1)

输出:

Step 0 Episode 65,100 loss=(-2.30202248835621)

最后结果

image-20230110210135973

其他效果,具体参考看这里跳转

(1)转圈效果

from time import sleep
list = ['|','/','—','\\']    #创建转圈效果的所有样式,'\\'是'\'本身
while 1:
    for i in list:
        print('\r正在加载中 %s '%i,end='')
        sleep(0.05)    #间隔0.05秒

img

(2)动态表情

from time import sleep
list1 = ['(づ。◕ᴗᴗ◕。)づ','(づ。—ᴗᴗ—。)づ','(づ。◕ᴗᴗ◕。)づ','(づ。—ᴗᴗ—。)づ','(づ。◕ᴗᴗ◕。)づ',
'(づ。◕ᴗᴗ◕。)づ','(づ。◕ᴗᴗ◕。)づ','(づ。◕ᴗᴗ◕。)づ','(づ。◕ᴗᴗ◕。)づ','(づ。◕ᴗᴗ◕。)づ']
#第一个动态表情图的所有样式
list2 = ['u~(@_@)~*','u~(@_@)~*','u~(@_@)~*','u~(@_@)~*','u~(@_@)~*',
'u~(@_@)~*','u~(—_—)~*','u~(@_@)~*','u~(—_—)~*','u~(@_@)~*']
#第二个动态表情图的所有样式
while 1:
    for a,b in zip(list1,list2):
        print('\r %s %s '%(a,b),end='')
        sleep(0.15)

img

(3)进度条

from time import sleep
while 1:
    for i in range(51):
        print('\r加载进度: [%-50s]%.2f%%  '%('#'*i,i*2),end='')
        sleep(0.05)

img

我们还可以为它加上颜色:

from time import sleep
while 1:
    for i in range(51):
        print('\r加载进度: [\033[32m%-50s\033[0m]\033[32m%.2f%%\033[0m  '%('#'*i,i*2),end='')
        sleep(0.05)

img

计算机视觉

图像裁剪

在计算机视觉中,通常将输入图像的大小调整为固定大小以便于模型处理。对于resnet34模型,它的输入尺寸是224x224。因此,我们需要对输入图像进行预处理,将其调整为224x224。但是,在将图像调整为目标大小之前,通常需要进行一些预处理操作来确保输入图像具有相同的特征。

在上面的示例中,首先将图像大小调整为256x256。这是因为图像通常具有不同的大小和宽高比,我们需要将其调整为统一的大小以确保输入图像具有相同的特征。然后,对调整后的图像进行中心裁剪为224x224,以确保图像的关键特征位于中心位置,这有助于提高模型的准确性。最后,将图像转换为张量并进行归一化,以便于模型进行处理。


文章作者: 马克图布
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 马克图布 !
评论
  目录