一些常用脚本

1. 读取显存剩余占用显卡

深度学习常需要进行参数对比实验,常见做法是记录每次程序运行时间,然后手动调节并运行程序。这对于我这种经常忘记时间的人不是很友好,可以用下面的脚本自动监测显存剩余,然后程序自动循环遍历参数即可。

1.1 比较傻的方式

import os
import sys
import time
cmd = 'bash run.sh'

def gpu_info():
    # gpu_status = os.popen('nvidia-smi | grep %').read().split('|')
    gpu_status = os.popen('nvidia-smi').read().split('|')
    # get gpu memory info
    gpu0_info, gpu1_info = gpu_status[23], gpu_status[35]  # 不同显卡显存信息位置略有不同
    gpu0_info, gpu1_info = gpu0_info.strip(), gpu1_info.strip()
    gpu0_info, gpu1_info = gpu0_info.split(" / "), gpu1_info.split(" / ")
    # remove 'MiB'
    gpu0_use_mem, gpu0_total_mem = gpu0_info[0][:-3], gpu0_info[1][:-3]
    gpu1_use_mem, gpu1_total_mem = gpu1_info[0][:-3], gpu1_info[1][:-3]
    gpu0_remain_mem, gpu1_remain_mem = int(gpu0_total_mem) - int(gpu0_use_mem), int(gpu1_total_mem) - int(gpu1_use_mem)
    gpu_remain_mem = [gpu0_remain_mem, gpu1_remain_mem]
    return gpu_remain_mem


def narrow_setup(interval=2):
    gpu_remain_mem = gpu_info()
    i = 0
    #while max(gpu_remain_mem) < 20000:  # set waiting condition
    while gpu_remain_mem[0] < 10000:  # set waiting condition
        gpu_remain_mem = gpu_info()
        i = i % 5
        symbol = 'monitoring: ' + '>' * i + ' ' * (10 - i - 1) + '|'
        gpu_memory_str = 'Remaining gpu memory: {} MiB |'.format(gpu_remain_mem)
        sys.stdout.write('\r' + gpu_memory_str + ' ' + ' ' + symbol)
        sys.stdout.flush()
        time.sleep(interval)
        i += 1
    gpu_index = gpu_remain_mem.index(max(gpu_remain_mem))
    sys.stdout.write("GPU Max Remain: {}".format(max(gpu_remain_mem)) + ' In GPU Number {}'.format(gpu_index))
    os.environ["CUDA_VISIBLE_DEVICES"] = "{}".format(gpu_index)
    print('\n' + cmd)
    os.system(cmd)


if __name__ == '__main__':
    narrow_setup()

这样只需要等待上一个程序跑完显存充足后自动运行下一个程序,在指定显卡上运行直接设置waiting condition为指定显卡即可,而多卡则可以设置检测到的显存空余最多的显卡,返回卡号并添加到cmd中执行即可。

1.2 升级版(读取CPU+内存+显存)

import psutil
import pynvml
import os
import re

def getMemCpuGPU():
    # 内存
    memory = psutil.virtual_memory()
    totalMemory = memory.total  # 总内存,单位为byte
    totalMemory = totalMemory * 1.0 / 1024 / 1024 / 1024  # 单位为G
    freeMemory = memory.available  # 可用内存
    freeMemory = freeMemory * 1.0 / 1024 / 1024 / 1024
    usedMemory = memory.used  # 使用的内存
    usedMemory = usedMemory * 1.0 / 1024 / 1024 / 1024
    print('内存剩余情况: {:0.2f}G/{:0.2f}G'.format(freeMemory, totalMemory))
    # CPU
    CPUPercent = psutil.cpu_percent()
    print('CPU使用率: {}%'.format(CPUPercent))
    # GPU
    pynvml.nvmlInit()
    handle = pynvml.nvmlDeviceGetHandleByIndex(0)  # 这里的0是GPU id
    meminfo = pynvml.nvmlDeviceGetMemoryInfo(handle)
    totalGPUMemory = meminfo.total * 1.0 / 1024 / 1024 / 1024
    freeGPUMemory = meminfo.free * 1.0 / 1024 / 1024 / 1024
    usedGPUMemory = meminfo.used * 1.0 / 1024 / 1024 / 1024
    print('显存剩余情况: {:0.2f}G/{:0.2f}G'.format(freeGPUMemory, totalGPUMemory))
    return freeMemory, totalMemory, CPUPercent, freeGPUMemory, totalGPUMemory

如果需要监控其他显卡,只需要将pynvml.nvmlDeviceGetHandleByIndex(0)中的0换成其他显卡序号即可;若监控多块显卡,加一个循环调用即可。

2. 多模式占用1~4显卡脚本

# author: ChronousZ
# contact: 1276298750@qq.com
import os
import sys
import time
 
cmd = 'python ./test_gpu.py'

 
def gpu_info():
    gpu_status = os.popen('nvidia-smi | grep %').read().split('|')
    # get gpu memory info
    gpu_status_mem = []
    gpu_remain_mem = []
    for gpu_status_item in gpu_status:
        if 'MiB' in gpu_status_item:
            gpu_status_mem.append(gpu_status_item)
    # print(gpu_status_mem)
    for gpu_status_mem_item in gpu_status_mem:
        gpu_used_mem = int(gpu_status_mem_item.split(" / ")[0].split("MiB")[0])
        gpu_total_mem = int(gpu_status_mem_item.split(" / ")[1].split("MiB")[0])
        gpu_remain_mem.append(gpu_total_mem - gpu_used_mem)
    return gpu_remain_mem
 
 
def narrow_setup(watchMethod, which_gpu, memory_th=6000, interval=2):
    gpu_remain_mem = gpu_info()
    # print(gpu_remain_mem)
    i = 0
    watching = True
    while watching:
        if watchMethod == 'single':  # set waiting condition
            if max(gpu_remain_mem) >= memory_th:
                watching = False
                gpu_index = str(gpu_remain_mem.index(max(gpu_remain_mem)))
                show_gpu_mem = [max(gpu_remain_mem)]
        elif watchMethod == 'double':
            if sorted(gpu_remain_mem, reverse=True)[1] >= memory_th:
                watching = False
                gpu_index = ''
                gpu_index += str(gpu_remain_mem.index(sorted(gpu_remain_mem, reverse=True)[0])) + ',' # max remain gpu
                gpu_index += str(gpu_remain_mem.index(sorted(gpu_remain_mem, reverse=True)[1])) # second max remain gpu
                show_gpu_mem = sorted(gpu_remain_mem, reverse=True)[:2]
        elif watchMethod == 'triple':
            if sorted(gpu_remain_mem, reverse=True)[2] >= memory_th:
                watching = False
                gpu_index = ''
                gpu_index += str(gpu_remain_mem.index(sorted(gpu_remain_mem, reverse=True)[0])) + ',' # max remain gpu
                gpu_index += str(gpu_remain_mem.index(sorted(gpu_remain_mem, reverse=True)[1])) + ',' # second max remain gpu
                gpu_index += str(gpu_remain_mem.index(sorted(gpu_remain_mem, reverse=True)[2])) # third max remain gpu
                show_gpu_mem = sorted(gpu_remain_mem, reverse=True)[:3]
        elif watchMethod == 'quadra':
            if sorted(gpu_remain_mem, reverse=True)[3] >= memory_th:
                watching = False
                gpu_index = ''
                gpu_index += str(gpu_remain_mem.index(sorted(gpu_remain_mem, reverse=True)[0])) + ',' # max remain gpu
                gpu_index += str(gpu_remain_mem.index(sorted(gpu_remain_mem, reverse=True)[1])) + ',' # second max remain gpu
                gpu_index += str(gpu_remain_mem.index(sorted(gpu_remain_mem, reverse=True)[2])) + ',' # third max remain gpu
                gpu_index += str(gpu_remain_mem.index(sorted(gpu_remain_mem, reverse=True)[3])) # fourth max remain gpu
                show_gpu_mem = sorted(gpu_remain_mem, reverse=True)[:4]
        elif watchMethod == 'special_gpu':
            if gpu_remain_mem[which_gpu] >= memory_th:
                watching = False
                gpu_index = str(which_gpu)
                show_gpu_mem = [gpu_remain_mem[which_gpu]]
        gpu_remain_mem = gpu_info()
        i = i % 5
        symbol = 'monitoring: ' + '>' * i + ' ' * (10 - i - 1) + '|'
        gpu_memory_str = 'Remaining gpu memory: {} MiB |'.format(gpu_remain_mem)
        sys.stdout.write('\r' + gpu_memory_str + ' ' + ' ' + symbol)
        sys.stdout.flush()
        time.sleep(interval)
        i += 1
    sys.stdout.write("\r" + "Watching Method: {}, GPU Memory Remain {} MiB in GPU Number {}".format(watchMethod, show_gpu_mem, gpu_index))
    os.environ["CUDA_VISIBLE_DEVICES"] = gpu_index
    # Here set the visiable GPU, direct set '0, 0,1, 0,1,3' etc in runing program
    print('\n' + cmd)
    os.system(cmd)
 
 
if __name__ == '__main__':
    watchMethod = 'double'
    # single gpu, double gpu, triple gpu, quadra gpu
    assert watchMethod in ['single', 'double', 'triple', 'quadra', 'special_gpu']
    # if you set watchMethod as 'special_gpu', then you must give the index of gpu for watching
    which_gpu = 0
    narrow_setup(watchMethod=watchMethod, which_gpu=which_gpu)

3. 定时运行脚本

做reid的同学可能会遇到多个程序同时在最终检索时计算距离矩阵导致内存爆炸的情况,而这个计算时间往往会占据很长的时间(query和gallery比较大的情况),因此多个程序必须错峰运行,为了自动进行可以使用下面的脚本。

#!/bin/bash
MIN_Value=`echo $1 | awk -F : '{print $1}'`
SEC_Value=`echo $1 | awk -F : '{print $2}'`
All_Sec=$[$MIN_Value*60+$SEC_Value]
for ((;All_Sec>=0;All_Sec--))
do
        MIN=$[All_Sec/60]
        SEC=$[All_Sec%60]
        echo  -ne "\rAfter  $MIN:$SEC is end!!"
        sleep 1
done
bash run.sh

运行时执行bash myContab.sh 180:02即可,:前是分钟数,后面为秒数

4. 浏览器倍数播放

浏览器空白处右击,选择检查, 然后console,输入下面代码后回车即可6倍数播放视频。

document.querySelector('video').playbackRate = 6; 

5. 统计文件大小

对于window下有时可能经常需要移动或者拷贝文件,如果遇上文件很大很多时可能导致速度很慢,尤其是与CV+NLP碰撞时,可能有一个隐藏得很深的目录下保存了很多模型或者没啥用的中间文件(比如某个目录下有几万张图像),可能这些文件并不重要,是可以删除的~。这时,让我们从中找出那些可以删除的文件其实很难(目录太多,太深)。于是,我们可以统计一下每个文件和目录下的文件大小,然后找到大的,文件多的删除掉即可。脚本如下:

import os
f_dir = os.path.abspath(os.path.dirname(__file__))
def get_dir_size(filePath):
    dir_size = 0
    if os.path.isfile(filePath):
        dir_size = os.path.getsize(filePath)
    elif os.path.isdir(filePath):
        for root, dirs, files in os.walk(filePath):
            dir_size += sum([os.path.getsize(os.path.join(root, name)) for name in files])
    else:
        pass
    return dir_size
def get_dir_file_num(filePath, file_num):
    if os.path.isfile(filePath):
        file_num += 1
    elif os.path.isdir(filePath):
        for _ in os.listdir(filePath):
            new_path = os.path.join(filePath, _)
            file_num = get_dir_file_num(new_path, file_num)
    else:
        pass
    return file_num
if __name__ == '__main__':
    basePath = r"D:\Users\Desktop\*"
    total_size = 0
    total_dir_file_num = 0
    total_sizs_dict = []
    for path in os.listdir(basePath):
        path = os.path.join(basePath, path)
        size = get_dir_size(path) / 1024 / 1024
        dir_file_num = get_dir_file_num(path, 0)
        total_sizs_dict.append([size, path, dir_file_num])
        print('{} 占用内存为 {:0.2f} M, 共有 {} 个文件'.format(path, size, dir_file_num))
        total_size += size
        total_dir_file_num += dir_file_num
    print("-" * 80)
    total_sizs_dict = sorted(total_sizs_dict, key=lambda x: x[0], reverse=True)  # 按照size大小排序
    topK = 10
    print("内存占用前 {} 的文件为:".format(topK))
    for i in range(min(len(total_sizs_dict), topK)):
        print('Top-{} {} 占用内存为 {:0.2f} M, 共有 {} 个文件'.format(i+1, total_sizs_dict[i][1], total_sizs_dict[i][0], total_sizs_dict[i][2]))
    print("-" * 80)
    print("{} 总共占用内存为 {:0.2f} M, 共有 {} 个文件".format(basePath, total_size, total_dir_file_num))


basePath填入需要统计的目录即可,比如我的一个目录如下:
在这里插入图片描述
大约11G,而且有一个目录下有55w文件,肯定是哪个数据集解压了~
此外,还会给出前K大的目录,方便用户清理~

6. 删除目录

有了上面统计某个目录下有很多小文件需要处理时(比如删除),对于windows来说,实在是一场灾难,会出奇的慢。而linux相对来说就快很多,可以直接rm -rf folder解决,但其实多到一定程度,对linux也很难处理,所以才会有大数据处理中的海量小文件处理的优化。我们这里拉回win下删除小文件,可以如下处理:

import shutil
import os

basePath = r"D:\Users\Desktop\*\reID"
for path in os.listdir(basePath):
    path = os.path.join(basePath, path)
    if os.path.isdir(path):
        print("正在删除 {}".format(path))
        shutil.rmtree(path)
    elif os.path.isfile(path):
        pass
    else:
        pass

在这里插入图片描述

其中,只需要在basePath中填入需要处理的目录即可,比如上图这里填入.../reID,就可以删除DukeMarket两个目录,而压缩包不会删除。虽然以上脚本处理的速度也还是挺慢的,但相对win的操作已经快很多了,因为这个慢是大数据面临的海量小文件的问题。而且是脚本是和linux那样把这块内存变成可写入 ,而不是像win那样放进回收站(没有删除,内存并没有空出来,只有清除回收站才会将这块内存变成可写入)

7. 读写txt

## 1. 一次性读全部内容:一次性读取文本中全部的内容,以字符串的形式返回结果
with open("test.txt", "r") as f:  # 打开文件
    data = f.read()  # 读取文件
    print(data)
    
## 2. 读取第一行内容:只读取文本第一行的内容,以字符串的形式返回结果
with open("test.txt", "r") as f:
    data = f.readline()
    print(data)

## 3. 列表:读取文本所有内容,并且以数列的格式返回结果,一般配合for in使用
with open("test.txt", "r") as f:
    for line in f.readlines():
        line = line.strip('\n')  # readlines会读到换行符,去掉列表中每一个元素的换行符
        print(line)

## 4. 写入txt文本
with open("test.txt","w") as f:
    f.write("这是个测试!")  # 自带文件关闭功能,不需要再写f.close()
    
## 读写模式
"""
r :   读取文件,若文件不存在则会报错
w:   写入文件,若文件不存在则会先创建再写入,会覆盖原文件
a :   写入文件,若文件不存在则会先创建再写入,但不会覆盖原文件,而是追加在文件末尾
rb,wb:  分别与r,w类似,但是用于读写二进制文件
r+ :   可读、可写,文件不存在也会报错,写操作时会覆盖
w+ :   可读,可写,文件不存在先创建,会覆盖
a+ :  可读、可写,文件不存在先创建,不会覆盖,追加在末尾
"""

8. 压缩文件到zip

import os

def writeAllFileToZip(filePath, zipFile):
    # 定义一个函数,递归读取filePath文件夹中所有文件,并塞进zipFile文件中,参数absDir表示文件夹的绝对路径
    for f in os.listdir(filePath):
        file = os.path.join(filePath, f)
        print(file, "压缩完成")
        if os.path.isdir(file):  # 判断是文件夹,继续深度读取
            zipFile.write(file)  # 在zip文件中创建文件夹
            writeAllFileToZip(file, zipFile)  # 递归操作
        else:  # 判断是普通文件,直接写到zip文件中
            zipFile.write(file)
    return

9. 远程文件传输

import paramiko  # 用于调用scp命令
from scp import SCPClient

def fileTransfer(local_path, remote_path, direction):
    """
    进行两台机器之间的文件传输
    :param local_path: 本地文件路径
    :param remote_path: 远程服务器文件路径
    :param direction: ['get', 'put'] 前者表示将remote_path传输到local_path, 后者则是将local_path传输到remote_path
    :return:
    """
    # 参数
    host = "*"  # 远程linux服务器ip地址
    port = * # 端口号
    username = "*"  # 远程服务器用户名
    password = "*"  # 远程服务器密码

    # 实例化SSHClient
    ssh_client = paramiko.SSHClient()
    # 自动添加策略,保存服务器的主机名和密钥信息,如果不添加,那么不再本地know_hosts文件中记录的主机将无法连接
    ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    # 连接SSH服务端,以用户名和密码进行认证
    ssh_client.connect(host, port, username, password)

    # 传输文件/文件夹
    scpclient = SCPClient(ssh_client.get_transport(), socket_timeout=60.0)
    assert direction in ['get', 'put']
    try:
        if direction == 'put':
            scpclient.put(local_path, remote_path, True)
        else:
            scpclient.get(remote_path, local_path, True)
    except FileNotFoundError:
        if direction == 'put':
            print("上传失败:" + local_path)
        else:
            print("下载失败:" + remote_path)
    else:
        if direction == 'put':
            print("上传成功:" + local_path)
        else:
            print("下载成功:" + remote_path)

    # # 执行cmd里的命令,stdout 为正确输出,stderr为错误输出
    # cmd = "touch ok.py;chmod +x ok.py"
    # stdin, stdout, stderr = ssh_client.exec_command(cmd)
    # print(stdout.read().decode("utf-8"))

    # 关闭SSHClient
    ssh_client.close()

10. 格式化输出

a = 2.63
b = 2
print("{:02d} {:03d} {:6.2f} {:6.3f} {:3.1f}".format(b, b, a, a, a))

"""
分别表示: 
  - 两位整数(不足用0补充)
  - 三位整数(不足用0补充)
  - 六位小数(小数点后占两位,小数点前不足用空格补充,小数点后不足用0做精确)
  - 六位小数(小数点后占三位,小数点前不足用空格补充,小数点后不足用0做精确)
  - 三位小数(小数点后占一位,小数点前不足用空格补充,小数点后不足用0做精确)
"""

# 输出:
"02 002 __2.63 _2.630 2.6"
# 注意其中_表示格式化输出占据的空格

11. conda虚拟环境

# 查看当前存在的虚拟环境
conda env list   # 方法1
conda info -e    # 方法2
# 创建虚拟环境
conda create -n ENV_NAME python=X.X    # ENV_NAME是环境名, X.X是python版本
# 激活指定虚拟环境
source activate ENV_NAME  # Linux
activate ENV_NAME         # Win
# 关闭虚拟环境
source deactivate ENV_NAME   # Linux
deactivate ENV_NAME          # Win
# 删除虚拟环境
conda remove -n ENV_NAME --all

12. 查看python-class的属性

有时我们调用第三方py包的时候会遇到不知道其封装的某个类有哪些属性,恰好有无法查看其源码(如pyd, pyc等文件)而且class还没写__doc__帮助文档时,可以用下面的方法查看其属性。

# -------------------------------
# 假设这部分源码不可见哈.......
class testClass(object):
    class_var = 1
    def __init__(self):
        self.name = 'xy'
        self.age = 2

    @property
    def num(self):
        return self.age + 10

    def fun(self):
    	pass
    def static_f():
    	pass
    def class_f(cls)
    	pass
# -------------------------------


# 方法1: `__dict__`
testClassInstance = testClass()
print(testClassInstance.__dict__)   #{'age': 2, 'name': 'xy'}   实例的__dict__属性
print(testClass.__dict__)    
"""
# 类testClass的__dict__属性
{
	'__dict__': <attribute '__dict__' of 'A' objects>, # 这里如果想深究的话查看参考链接
	'__module__': '__main__',               # 所处模块
	'num': <property object>,               # 特性对象 
	'class_f': <function class_f>,          # 类方法
	'static_f': <function static_f>,        # 静态方法
	'class_var': 1, 'fun': <function fun >, # 类变量
	'__weakref__': <attribute '__weakref__' of 'A' objects>, 
	'__doc__': None,                        # class说明字符串
	'__init__': <function __init__ at 0x0000000003451AC8>}
"""

# 方法2: `dir()`
print(dir(testClass))
"""
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'class_f', 'class_var', 'fun', 'level1', 'level2', 'name', 'num', 'static_f']
"""

# 两者区别(`testClass.__dict__` vs `dir(testClass)`):
# 1. 实例的`__dict__`仅存储与该实例相关的实例属性,每个实例的实例属性互不影响
# 2. 类的`__dict__`存储所有实例共享的变量和函数(类属性,方法等),类的__dict__并不包含其父类的属性
# 3. `dir()`函数会自动寻找一个对象的所有属性(包括其父类`object`的),`__dict__`中的属性是`dir()`的子集

13. numpy不使用省略号和科学计数法表示

  • 一般在debug的时候可能用的比较多
  • 解决方法:
    • 一般在数组维度较大时,显示时就会使用省略号代替中间结果。正确显示的解决方法是:np.set_printoptions(threshold = np.inf)
    • 还有一个问题是数组里面的数值会以科学计数法表示。正常显示的解决方法是:np.set_printoptions(suppress = True)

版权声明:本文为qq_35226955原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。