Python Lib
GDB Debugging Python
inspect threads
(gdb) info threads
get a picture of all threads:
(gdb) t a a py-list
# or
(gdb) thread apply all py-list
walk through the stack
(gdb) py-up
or
(gdb) py-down
full stack trace
(gdb) py-bt
Multiprocessing
Handle Orphan Process
An orphan process is a computer process whose parent process has finished or terminated, though it remains running itself. An orphan process is created when the parent of a child process is terminated.
Some ways that a parent process may be terminated leaving the child process orphaned include:
- Parent process finishes.
- Parent raises an uncaught error or exception.
- Parent process calls sys.exit() or os._exit().
- The Process.terminate() or Process.kill() methods are called on the parent.
- A process calls os.kill() with the parent's PID.
- Process is killed externally, e.g. via the kill command.
Some solutions could be:
- Other processes keep a reference to processes that could be orphaned, e.g. Process instances.
- Store process identifiers (PIDs) for child processes that could be orphaned, e.g. in a file.
- Child processes can check if they are orphaned and take action accordingly.
Check if a Process is Orphaned
# check for no parent process
if multiprocessing.parent_process() is None:
print('Orphaned')
Alternatively, a child process may have a multiprocessing.Process instance returned from the multiprocessing.parent_process() function, but the process may not be running. In which case, the process is orphaned
# get the parent process
parent = multiprocessing.parent_process()
if parent is not None and not parent.is_alive():
print('Orphaned')
# return True if the current process is orphaned, False otherwise
def is_orphan():
# get the parent process
parent = parent_process()
# check if orphaned
if parent is None or not parent.is_alive():
return True
# not orphaned
return False
pytorch
check version
import torch
import torch.nn as nn
import torchvision
print(torch.__version__)
print(torch.version.cuda)
print(torch.backends.cudnn.version())
print(torch.cuda.get_device_name(0))
Use GPU
Single GPU:
# Device configuration
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
Multi-GPU:
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1'
in CLI:
CUDA_VISIBLE_DEVICES=0,1 python train.py
clear GPU cache:
torch.cuda.empty_cache()
reset gpu
$ nvidia-smi --gpu-reset -i [gpu_id]
torchvision
PIL Image/ numpy.ndarray to pytorch tensor
torchvision.transform.ToTensor
transforms.ToTensor()
Normalize
torchvision.transforms.Normalize(mean, std, inplace=False)
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
squeeze and unsqueeze
squeeze sample:
import torch
x = torch.rand(3, 1, 20, 128)
x = x.squeeze() #[3, 1, 20, 128] -> [3, 20, 128]
unsqueeze sample:
x2 = torch.rand(1, 1, 20, 128)
x2 = x2.squeeze(dim=1) # [1, 1, 20, 128] -> [1, 20, 128]
tensor info
tensor = torch.randn(3,4,5)
print(tensor.type()) # 数据类型
print(tensor.size()) # 张量的shape,是个元组
print(tensor.dim()) # 维度的数量
set tensor name
# 在PyTorch 1.3之前,需要使用注释
# Tensor[N, C, H, W]
images = torch.randn(32, 3, 56, 56)
images.sum(dim=1)
images.select(dim=1, index=0)
# PyTorch 1.3之后
NCHW = [‘N', ‘C', ‘H', ‘W']
images = torch.randn(32, 3, 56, 56, names=NCHW)
images.sum('C')
images.select('C', index=0)
# 也可以这么设置
tensor = torch.rand(3,4,1,2,names=('C', 'N', 'H', 'W'))
# 使用align_to可以对维度方便地排序
tensor = tensor.align_to('N', 'C', 'H', 'W')
Data type transform
# 设置默认类型,pytorch中的FloatTensor远远快于DoubleTensor
torch.set_default_tensor_type(torch.FloatTensor)
# 类型转换
tensor = tensor.cuda()
tensor = tensor.cpu()
tensor = tensor.float()
tensor = tensor.long()
numpy array to Pytorch Tensor:
- torch.Tensor()
- torch.from_numpy()
torch.Tensor() 는 Numpy array의 사본일 뿐이다. 그래서 tensor의 값을 변경하더라도 Numpy array자체의 값이 달라지지 않는다.
import torch
import numpy as np
a=np.array([1,2,3,4])
b=torch.Tensor(a)
print('output tensor:',b)
b[0]=-1
print('It cannot change np.array:',a)
torch.from_numpy()는 자동으로 input array의 dtype을 상속받고 tensor와 메모리 버퍼를 공유하기 때문에 tensor의 값이 변경되면 Numpy array값이 변경된다.
a=np.array([1,2,3,4])
b=torch.from_numpy(a)
print('output tensor:',b)
b[0]=-1
print('It can change np.array:',a)
torch Tensor to numpy
import torch
import numpy as np
a = torch.rand(3,3)
b = a.numpy()
or
ndarray = tensor.cpu().numpy()
#tensor = torch.from_numpy(ndarray).float()
#tensor = torch.from_numpy(ndarray.copy()).float() # If ndarray has negative stride.
Torch tensor to PIL.Image
# pytorch中的张量默认采用[N, C, H, W]的顺序,并且数据范围在[0,1],需要进行转置和规范化
# torch.Tensor -> PIL.Image
image = PIL.Image.fromarray(torch.clamp(tensor*255, min=0, max=255).byte().permute(1,2,0).cpu().numpy())
image = torchvision.transforms.functional.to_pil_image(tensor) # Equivalently way
# PIL.Image -> torch.Tensor
path = r'./figure.jpg'
tensor = torch.from_numpy(np.asarray(PIL.Image.open(path))).permute(2,0,1).float() / 255
tensor = torchvision.transforms.functional.to_tensor(PIL.Image.open(path)) # Equivalently way
np.array to PIL.Image
image = PIL.Image.fromarray(ndarray.astype(np.uint8))
ndarray = np.asarray(PIL.Image.open(path))
tensor transform
# 在将卷积层输入全连接层的情况下通常需要对张量做形变处理,
# 相比torch.view,torch.reshape可以自动处理输入张量不连续的情况。
tensor = torch.rand(2,3,4)
shape = (6, 4)
tensor = torch.reshape(tensor, shape)
Flip tensor
e.g.
# [0 1 2 3 4 5 6 7 8 9]
# to [9 8 7 6 5 4 3 2 1 0]
# pytorch不支持tensor[::-1]这样的负步长操作,水平翻转可以通过张量索引实现
# 假设张量的维度为[N, D, H, W].
tensor = tensor[:,:,:,torch.arange(tensor.size(3) - 1, -1, -1).long()]
copy tensor
# Operation | New/Shared memory | Still in computation graph |
tensor.clone() # | New | Yes |
tensor.detach() # | Shared | No |
tensor.detach.clone()() # | New | No |
concate tensor
'''
注意torch.cat和torch.stack的区别在于torch.cat沿着给定的维度拼接,
而torch.stack会新增一维。例如当参数是3个10x5的张量,torch.cat的结果是30x5的张量,
而torch.stack的结果是3x10x5的张量。
'''
tensor = torch.cat(list_of_tensors, dim=0)
tensor = torch.stack(list_of_tensors, dim=0)
Numpy
squeeze
x = np.array([[[0], [1], [2]]])
np.squeeze(x).shape
np.squeeze(x, axis=0).shape
np.squeeze(x, axis=1).shape
Create random array
# a random integer in the given range
rand_int = np.random.randint(5,10)
# random numpy array of shape (4,5)
rand_int2 = np.random.randint(10,90,(4,5))
rand_int3 = np.random.randint(50,75,(2,2), dtype='int64')
randint()
parameters:
Low
lower limit of the range, High
upper limit of the range, Size
shape of the array, dtype
datatype of the values.
Save array as txt file
a = np.array([1, 2, 3, 4])
np.savetxt('test1.txt', a, fmt='%d')
b = np.loadtxt('test1.txt', dtype=int)
a == b
or
a.tofile('test2.dat')
c = np.fromfile('test2.dat', dtype=int)
c == a
or There is a platform independent format for NumPy arrays:
np.save('test3.npy', a) # .npy extension is added if not given
d = np.load('test3.npy')
a == d
Statistics
sum an array:
import numpy as np
a=np.array([0.4,0.5])
b=np.sum(a)
or
import numpy as np
a=np.array([[1,4],[3,5]])
b=np.sum(a,axis=0)
mean
numpy.mean(arr)
variance
numpy.var(arr)
standard deviation
numpy.std(arr)
count elements
cnt = np.count_nonzero(hue_roi > 0)
MSE
np.square(np.subtract(A, B)).mean()
padding 2D array
up=2
down=2
left=2
right=2
np.pad(img, ((up,down),(left,right)), 'constant', constant_values=0)
show array's all elements
import sys
import numpy
numpy.set_printoptions(threshold=sys.maxsize)
Change data type:
img = data.astype(np.uint8)
Change element
a[a < 0] = 0
np.where(a < 0, 0, a)
np.clip(a, 0, 4)
matplotlib
Preinstall:
$ sudo apt-get install python3-tk
Save plot:
import matplotlib.pyplot as plt
plt.plot([1,2,3], [5,7,4])
plt.savefig("mygraph.png")
plot numpy array
Show array distribution:
import numpy as np
import matplotlib.pyplot as plt
n, bins, patches = plt.hist(np_array)
plt.show()
or
from matplotlib import pyplot as plt
import numpy as np
a = np.array([22,87,5,43,56,73,55,54,11,20,51,5,79,31,27])
plt.hist(a, bins = [0,20,40,60,80,100])
plt.title("histogram")
plt.show()
OS LIB
Generate Dir
from pathlib import Path
Path("/my/directory").mkdir(parents=True, exist_ok=True)
Tensorflow 1.x
Limit Gpu Growth
gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=0.333)
sess = tf.Session(config=tf.ConfigProto(gpu_options=gpu_options))
Auto Adjuct Gpu Growth
config = tf.ConfigProto()
config.gpu_options.allow_growth = True
session = tf.Session(config=config)
Troubleshooting
Flask uses multiple threads. The problem you are running into is because the tensorflow model is not loaded and used in the same thread. One workaround is to force tensorflow to use the gloabl default graph .
solution: Add this after you load your model
global graph
graph = tf.get_default_graph()
And inside your predict
with graph.as_default():
y_hat = keras_model_loaded.predict(predict_request, batch_size=1, verbose=1)
Flask
Disable the reloader
run(use_reloader=False)