0%

深度学习常用词汇及常用编程语法--不断更新中

之前说要更新一下本科期间做的一些东西,然后再讲解一下加速器如何在FPGA上搭建,结果太忙了。。。。木得时间,后面有时间慢慢补上。

最近读了faster rcnn和yolo的源码,里面用到的一些函数自己不常用或者用法新奇或者有时候自己忘掉的,而且最先读faster rcnn,two-stage的源码的确比较复杂,原理很快看懂,读源码花了几天才搞明白,写篇博客不断更新记录一下遇到的一些函数和用法,防止遗忘,后续手机端查看也方便。跑faster rcnn深有感触,这玩意不是给普通人玩的,backbone用mobilenet v2情况下debug一下用10s多,内存耗掉四个g,要是restnet50就更夸张了,还是老老实实等服务器吧。。。(更新:拿到了,4块3090)

常用名词

backbone:主干,object detection中提取特征的cnn网络简称backbone

bounding box :边界框,object detection中框住物体的边界框

ground truth:目标的真实位置

logits:意思是输入时候不需要sigmoid概率化

简称

fpn:feature pyramid network 图像金字塔网络,多个遍历的窗口

rpn:region proposal network 区域提名网络

Pytorch & Numpy基础原理类

别人讲的太好了,所以有些就直接放链接了。

dir(类)可以查看类中的方法和属性,比如range(10)打印不可视化,可以dir(range(10)),可以看到有str和repr方法,但其实这两个打印的都不可视化,可以用list强制转换就可以打印了(一些自定义类中,可以自己重写__len__,这样就可以使用len()函数了)。

1、广播机制。

广播机制主要是在两个tensor/array进行运算时候,维度不一时候用到的机制,由于广播机制元素是从内往外扩充的,所以两个tensor/array之间的维度大小从内到外要一致,不一致的两个tensor/array至少有一个维度为1或者有一个压根没那个维度。

具体介绍的链接如下:

2个规则弄懂numpy的broadcast广播机制 - 知乎 (zhihu.com)

2、存储和视图。

torch中经常出现共享内存,只是改变视图和stride,而不分配新的内存空间,比如转置、expand等,可以用tensor.clone()来拷贝一份得到新的内存空间,共享内存很大程度是为了应对深度学习超大的参数量,这样不需要新的内存空间,也不需要变动内存存储顺序,可以起到节约空间和加速运行的效果。具体介绍如下:

由浅入深地带你了解分析张量

torch中可以有很多用.来进行调用的方法,可以帮我们查看一些信息,显然就是因为torch底层大多数是以面向对象的类封装的,所以arrya用点调用方法显然比tensor少。

tensor.size(),可以得到其shape

tensor.len(),这个真没有,其实不是没有,只不过用的是tensor.len而已,可以用len(tensor),len内部会调用tensor.len,

善用?查看函数

Pytorch & Numpy

以下函数都在Python3中得到验证

0、tensor生成

例如下:

1
2
3
4
5
6
a=torch.randn((2,3))#生成2行3列的正态分布随机数
b=torch.randint(3,10,(4,4))#Returns a tensor filled with random integers generated uniformly between :attr:`low` (inclusive) and :attr:`high` (exclusive).均匀分布,产生low-high的整数,只填一个数默认作为high,low默认是0
c=torch.randperm(5)#Returns a random permutation of integers from ``0`` to ``n - 1``,返回一个0到n-1的随机排序.可以用于随机采样样本,比如采样前百分之70。
d=torch.rand((2,3))#2行3列的[0,1)的均匀分布,uniform distribution
e=torch.Tensor([[1,2,3],[4,5,6]])
print(a,"\n",b,"\n",c,"\n",d,"\n",e)

输入如下,其中torch.randint的三个参数含义分别是low,high和size(即shape),

1
2
3
4
5
6
7
8
9
10
11
tensor([[ 0.8787,  0.5344,  0.3134],
[ 0.9224, -1.2589, 1.7708]])
tensor([[9, 5, 4, 4],
[3, 7, 4, 8],
[8, 6, 9, 9],
[9, 8, 5, 8]])
tensor([1, 4, 3, 2, 0])
tensor([[0.1877, 0.8270, 0.3400],
[0.2405, 0.6493, 0.7478]])
tensor([[1., 2., 3.],
[4., 5., 6.]])

1、zip函数

python自带函数 作用:将np/tensor/dict/list等可迭代对象元素组合,这个元素是指第0维的元素。在目标追踪网络中常出现,用于拼接x、y、w、h等等。其格式:zip``(iterable1,iterable2, …),即输入的是可迭代对象。

例子

1
2
3
4
5
6
a=[1,2,3]
b=[4,5,6]
c=zip(a,b)
print(c,type(c))
print(list(c))
print(c)

结果如下,直接print其实调用的是魔法函数str,由于未重写该方法,所以其和repr方法输出是一致,就是类名 + object at + 地址。而通过list函数,可以zip类的结果变成可视化的list列表,列表中每个元素都是拼接的值,值得注意的是,在list(c)后,不知道list函数中调用了c自身的某种方法,但这种方法改变了c的某些元素值,使得list(c)再list(c)时候,得到的是一个空列表,所以这个c传递更类似于引用传递,改变了c自身。

1
2
<zip object at 0x000001B48B796200> <class 'zip'>
[(1, 4), (2, 5), (3, 6)]

上述是可迭代对象list,下面测试一下可迭代对象dict:

1
2
3
4
5
6
7
#v1,v2,v3可是是任何可迭代对象,如:字符串、列表、元祖、字典
v1 = { 1 : 11 , 2 : 22 } #此处可迭代对象为字典
v2 = { 3 : 33 , 4 : 44 }
v3 = { 5 : 55 , 6 : 66 }

v = zip (v1,v2,v3) #压缩
print ( list (v))

结果如下,可以看出,对于字典,压缩组合是对于索引号的。(通过索引号索引值,用法和np类似)

1
[(1, 3, 5), (2, 4, 6)]

通过zip(*压缩组合元素)可以进行解压会原来样式(未必是完全一样,因为压缩的时候可能就不完整)。例:

1
print(list(zip(*zip (v1,v2,v3))))

结果如下,为什么不直接list(zip(*v))呢?上面说过了,经过list后就会改变原值,会变成空元素,压缩得到的也是空的。

1
[(1, 2), (3, 4), (5, 6)]

zip由于是组合,list后可以看出,其是可以变成list列表的,而list是Iterable的,即可迭代对象,自然可以用for,而list中元素都是一个个元组,元组单个元素赋值给单个变量,可以通过逗号进行(要么一个变量直接接受的赋值是一个元组,要么变量个数就是元组内元素个数,否则报错),如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
a=[1,2,3,4]
b=[4,5,6,7]
c=[1,3,5,7]
for i in zip(a,b):
print(i)
for i,j in zip(a,b):
print(i,j)
for i,j,k in zip(a,b,c):
print(i,j,k)
(1, 4)
(2, 5)
(3, 6)
(4, 7)
1 4
2 5
3 6
4 7
1 4 1
2 5 3
3 6 5
4 7 7

1.1、元组赋值方式

元组赋值方式,要么直接赋值给一个变量,那个变量就是接收到一个元组,即那个变量也是元组方式,要么接收值的变量个数和元组内元素数量一样(否则报错),变量之间用逗号隔开。

2、for循环使用方法

for常用功能两个:循环遍历和列表生成式 (其实列表生成式本质也是循环遍历,都用到了iternext方法,即迭代器生成和不断获取下一个元素方法)。

2.1、循环遍历

循环遍历常用的 for i in range(start,end,stride):不用细讲(需要注意的是,python语法中需要起始终止位置的,似乎从来不包括end的,比如切片,和verilog区别度显然)。此处主要想讲的是循环遍历的原理,for循环中,起始本质是使用了iter制造迭代器,然后用next方法不断顺序的读取下一个数据,不可回退,直至最后无元素可读,此刻抛出StopIteration 异常(这个异常估计就是try except处理了),然后退出。下面介绍一下迭代器等。

2.1.1、Iterable

Iterable即可迭代对象,循环遍历的对象必须是可迭代对象,其是一个元素个数明确且可迭代的对象(可迭代即可遍历),常用的有list、np、tensor、str、tuple、st、dict等

可迭代对象自带iter方法,通过iter即可得到迭代器Iterator(迭代器),比如a是个list,则可以通过a.lter调用迭代器方法,不过其实这个方法不需要我们调用,for的时候会自行调用。这里可以调用一下试试,例:

1
2
a=np.zeros((1,2))
print(a.__iter__)

得到的结果如下:其中method-wrapper的意思是”包装的方法”,后面就是数据类型 + object(物体,对象)+at+地址。

1
<method-wrapper '__iter__' of numpy.ndarray object at 0x000001B48BF1D0D0>

2.1.2、Iterator

Iterator即迭代器,Iterable对象通过iter方法可以得到Iterator(注意得到Iterator并不是返回,需要得到返回Iterator类型的,用iter()强制转换),Iterable即可迭代对象是一个元素个数已知且可遍历的对象,通过iter方法就得到了个数未知但依旧可遍历的对象Iterator,然后通过next进行遍历Iterator,next方法不断往下取数据,无数据取出时抛出StopIteration 异常停止迭代。

其实可以看出Iterator和Iterable很像,其实Iterable调用iter方法得到的Iterator,是一个继承Iterable的子类

2.1.3、判别Iterable和Iterator

判断一个变量的类型,可以通过isinstance()或者type()函数,但两者有所区别,type不认为子类是和父类是同一个类型,但isinstance则认为子类属于父类的类型,父类不属于子类的类型。推荐使用isinstance,因为平时往往认为子类和父类是一个类别的。(instance中是子类属于父类,父类不是子类,针对父类的函数,子类都可以传进去,还可以正确运行:看缪雪峰的面向对象章,这就是类的多态,)

isinstance例子如下

1
2
a=1
print(isinstance(a,int),isinstance(a,str),isinstance(a,dict),isinstance(a,(int,str,dict,list)))

结果如下,判别属于某个类别,isinstance第二个元素可以是元组。

1
True False False True

在看一下isinstance和type区别,如下

1
2
3
4
5
class A():
pass
class B(A):
pass
print(isinstance(A(),A),isinstance(B(),A),type(B())==A)

结果如下:

1
True True False

下面看一下迭代器的类别:

1
2
3
4
5
from collections import Iterator,Iterable
a=np.zeros((1,2))
print(isinstance(a,Iterable))
print(isinstance(a,Iterator))
print(len(a),a.__len__())

结果如下,np数组是可迭代对象,可以看出父类不属于子类;同时前面也提过,可迭代对象可以有长度属性,其实len()的本质就是调用了Iterable中的len方法,不过不加括号的话只会显示方法+类+object+at+地址(具体原因不知)。

1
2
3
True
False
1 1

下面可以看出子类输入父类,通过iter函数即可强制将变量转成迭代器

1
2
3
4
a1=iter(a)#iter转换成迭代器
print(isinstance(a1,Iterable))
print(isinstance(a1,Iterator))
print(len(a1))

结果如下,可以看出a1既属于可迭代对象,也属于迭代器,即子类既是属于子类也属于父类,用len的时候,报错了,因为迭代器长度是未知的,无法用len方法,只能用next方法。

1
2
3
4
5
6
7
8
9
10
True
True
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-163-dff46c0da30e> in <module>
2 print(isinstance(a1,Iterable))
3 print(isinstance(a1,Iterator))
----> 4 print(len(a1))

TypeError: object of type 'iterator' has no len()

记住需要返回迭代器得用iter(),需要返回取迭代器值的,用next,直接调用iternext没有想要的返回值。

下面看一下迭代器利用next取值:

1
2
3
4
5
6
a2=np.arange(0,10,1).reshape(2,-1)
print(a2)
a2=iter(a2)
print(next(a2))
print(next(a2))
print(next(a2))

结果如下,可以看出,通过next方法不断往下取值,当没有值可取时,抛出StopIteration

1
2
3
4
5
6
7
8
9
10
11
12
[[0 1 2 3 4]
[5 6 7 8 9]]
[0 1 2 3 4]
[5 6 7 8 9]
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-182-473194238b9f> in <module>
4 print(next(a2))
5 print(next(a2))
----> 6 print(next(a2))

StopIteration:

2.1.4 for本质

for的本质就是Iterator通过不断调用next()实现的。

1
2
3
a=np.arange(0,5,1)
for i in a:
pass

这个for循环等价于如下:

1
2
3
4
5
6
a1=iter(a)
while True:
try:
i=next(a1)
except StopIteration:
break

之前说过了,next是不可以回退的,所以一个Iterator只能遍历一次即用不了了,但Iterable是可以多次遍历的,所以每次for时都用到iter,每次iter都会得到一个新的Iterator,再next就好了。

2.2、列表生成式、集合生成式、字典生成式

列表生成式如下:

1
2
3
a=np.arange(4)
b=[i for i in a]
print(b)

输出如下:

1
[0, 1, 2, 3]

字典生成式如下,其中items()方法是输入字典类型,返回list形式的可迭代对象。

1
2
3
4
5
#d = {key: value for (key, value) in iterable}
d1 = {'x': 1, 'y': 2, 'z': 3}
d2 = {k: v for (k, v) in d1.items()}
print(d2)
print(d1.items())

输出如下:

1
2
{'x': 1, 'y': 2, 'z': 3}
dict_items([('x', 1), ('y', 2), ('z', 3)])

集合生成式:

1
2
3
#集合生成式
s1={x for x in range(10)}
print(s1)#集合无序性?所以没法切片

输出如下:

1
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

谈到了各类生成式,却没说元组生成式,貌似按照上述的做法,在括号内进行for得到的就是元组生成式,然而其实不是这样的,其实得到的是Generator。下面讲一下生成器

2.2.1、Generator生成器

先来看一下用法:

1
2
3
#生成器
a=(i**2 for i in [1,2,3,4])
print(a)

输出如下,可以看出显然得到的不是一个元组,而是一个generator

1
<generator object <genexpr> at 0x000001B48B769970>

生成器和迭代器很像,我们用next试试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
print(next(a))
print(next(a))
print(next(a))
print(next(a))
print(next(a))
1
4
9
16
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-194-2251f521890f> in <module>
3 print(next(a))
4 print(next(a))
----> 5 print(next(a))

StopIteration:

似乎这个和迭代器没有区别呀,似乎就是含有1,4,9,16的迭代器。

其实可以理解为生成器Generator就是特殊的迭代器,只不过其生成器一种迭代遍历过程中才计算的迭代器,就是说它存储的是1,2,3,4,在迭代过程中,才进行了平方操作,即next取值的时候进行了计算,可以理解为:生成器的元素在访问前不会生成,只有当访问时才会生成;如果继续向后访问,那么当前的元素会销毁,这个也可以理解,毕竟next是不可以回头的,之前的数据没有意义,所以销毁节约内存。而生成器的一种生成方式是将列表生成式改为小括号包裹。

下面谈一下生成器的本质(引用了):

  • 生成器本质上是一个函数
  • 当一个生成器被调用时,它返回一个生成器对象,而不用执行该函数。 当第一次调用 next()方法时,函数向下执行,如果遇到yield则返回 yield 后面的值。 再次调用next()方法时,函数从上次结束的位置继续向下执行,如果遇到yield则返回 yield 后面的值。
  • 可以使用yield来定义一个生成器。
1
2
3
4
5
6
7
8
9
10
11
12
13
print("\n----使用yield生成generator-------")
def ge():
print("第一次yield")
yield 1
print("第二次yield")
yield 2
print("第三次yield")
yield 3
print(type(o))
o = ge()
print(next(o))
print(next(o))
print(next(o))

结果如下,可以看出o是一个生成器。即生成器其实是可以通过yield关键字来得到。

1
2
3
4
5
6
7
8
----使用yield生成generator-------
<class 'generator'>
第一次yield
1
第二次yield
2
第三次yield
3
  • 生成器本质上是一个函数,如果想要获取这个函数的返回值,我们需要使用异常捕获来获取这个返回值:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def fib(max):
n,a,b = 0,0,1
while n <max:
yield b
a,b =b,a+b
n = n+1
return 'done'

print("\n-----尝试获得函数返回值------")
gg=fib(6)
while True:
try:
x=next(gg)
print("g:",x)
except StopIteration as e:
print('返回值等于:',e.value)
break
-----尝试获得函数返回值------
g: 1
g: 1
g: 2
g: 3
g: 5
g: 8
返回值等于: done
  • 既可以使用next()来迭代生成器,也可以使用for来迭代:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def ge():
print("第一次yield")
yield 1
print("第二次yield")
yield 2
print("第三次yield")
yield 3
o = ge()

print("\n---迭代generator的方法--------")
for x in o:
print(x)#相当于进入到generator函数中,执行下去并得到返回值
---迭代generator的方法--------
第一次yield
1
第二次yield
2
第三次yield
3

至此for结束,参考链接如下:

Python笔记整理 迭代器和生成器

字典生成式、集合生成式、生成器

3、拼接函数和分割函数、增加元素等等

3.1、torch.stack会增加维度,传入时候需要是tensor组成的元组或列表,普通列表和元组不行,如(a,a)或[a,a],扩充的维度如果是n,那么就是对n-1维度下的对应位置的元素拼接起来,然后加个维度。(np里也有stack,用法略有不同)

如下:

1
2
3
a=torch.tensor([[[1,2,3],[4,5,6]]])
b=torch.stack((a,a),dim=3)
print(b)

结果如下,dim=3即是在第二维度后面进行扩充,将第二维度下面的元素对应位置组起来加个维度。值得注意的是,torch中习惯用dim,而np习惯用axis。

1
2
3
4
5
6
7
tensor([[[[1, 1],
[2, 2],
[3, 3]],

[[4, 4],
[5, 5],
[6, 6]]]])

如果dim=2,结果如下,即是在第一维度后面加一个维度,则是将第一维度下的元素对应位置拼接起来组成一个维度。

1
2
3
4
5
tensor([[[[1, 2, 3],
[1, 2, 3]],

[[4, 5, 6],
[4, 5, 6]]]])

append

3.2、torch.cat

cat不增加维度,传入时候需要是tensor组成的元组或列表,普通列表和元组不行,例子:cat([a,b,c],dim=1)。对于维度n,cat只是对该维度内元素进行顺序拼接,不增加维度。

如下:

1
2
3
4
a=torch.Tensor([1,2,3])
b=torch.Tensor([1,2,3])
c=torch.cat([a,b],dim=0)
print(c)

结果如下:

1
tensor([1., 2., 3., 1., 2., 3.])

由于cat是顺序拼接,其实上述的这个结果很容易用其他方式实现,比如如下:

1
2
print(torch.Tensor(np.append(np.array(a),np.array(b))))
print(torch.Tensor([list(a),list(b)]).reshape(-1))

结果如下,可以看出通过append或者reshape都可以实现,但是torch中好像没有append函数,所以就先用np作为中介了。

1
2
tensor([1., 2., 3., 1., 2., 3.])
tensor([1., 2., 3., 1., 2., 3.])

在torch中有cat,numpy中自然有对应的函数,其就是np.concatenate((a,b),axis = 1),功能和torch.cat一致,与stack的区别仅在于没有扩充维度罢了。最典型的维度变换,cifar-10读进来的图片是batch*3*32*32,即一个数据有3072个点,前1024个为r,后1024个为g,最后1024个为b,所以读入后需要reshape(32,32,3),不能是reshape(3,32,32),因为numpy的reshape是按顺序来的,这样就错了。reshape(32,32,3)后,我们希望可以imshow观测,imshow需要的格式是(32,32,3)这个3代表rgb,所以我们在得到a=img.reshape(3,32,32)后,先进行a0=a[0,:,:],a1=a[1,:,:],a1=a[2,:,:],拆开3个通道,再b0=a0.reshape(32,32,1),b1=a1.reshape(32,32,1),b2=a2.reshape(32,32,1),之所以多弄一个维度是为了后续拼接,然后np.concatenate((b0,b1,b2),axis=2)即可得到(32,32,3)的形状。

3.3、split函数

第二个参数是表明其在第几个维度上分割,第一个元素代表分割的步长,函数返回的是元组类型,元组中元素是tensor类型。比如faster rcnn中,a的shape代表[batch_size,所有预测特征层anchors总数],为了把每个预测特征层分开,就可以如下操作,假设第一个预测特征层anchor总数为3,第二个预测特征层anchors是2;此法适合之前拼接时候记住个数的,然后在分割开。

1
2
3
4
5
import torch
a=torch.tensor([[1,2,3,4,5],[6,7,8,9,10]])
b=[3,2]
c=a.split(b,1)
print(a,"\n",c)

输出如下,说明分割是形参传递,并非引用,不改变原始a。返回的结果是元组形式。元组和list,tensor,np都是可迭代格式。

1
2
3
4
5
tensor([[ 1,  2,  3,  4,  5],
[ 6, 7, 8, 9, 10]])
(tensor([[1, 2, 3],
[6, 7, 8]]), tensor([[ 4, 5],
[ 9, 10]]))

torch中split和numpy不一样,numpy中split是按照下标切的,如下的[0,1]就是按照下标0及其之前切成一个,0到1之间切成一个,剩下的切作为一个

1
print(np.split(np.array([[1,2,3],[4,5,6]]),[0,1]))

输出如下,可以看出其得到的是列表,而torch是元组。

1
[array([], shape=(0, 3), dtype=int32), array([[1, 2, 3]]), array([[4, 5, 6]])]

3.4、分割 tensor.unbind

tensor.unbind,比如[[1,2,3],[4,5,6]],则a.unbind(1)即在维度1上分割,得到[1,4],[2,5],[3,6],其是把设定维度上每个元素分割开,得到一个元组。

例如下:

1
2
3
4
a=torch.Tensor([[1,2,3],[4,5,6]])
print(a.unbind(0),"\n",a.unbind(1))
(tensor([1., 2., 3.]), tensor([4., 5., 6.]))
(tensor([1., 4.]), tensor([2., 5.]), tensor([3., 6.]))

4.维度变化

1、tensor.fltten(array的用法不一样)

flatten(0,-2)即代表从第0维度一直拉平到倒数第二维度截止,拉平的元素以倒数第二个维度的元素为一个整体,即只有倒数第二个维度的元素作为总体进行拉平,拉平应该是这样格式[[1,2,3],[4,5,6],[7,8,9]]。

flatten(1)代表从1维度一直拉平到最后。

例如下:

1
2
3
4
5
a=torch.Tensor([[1,2,3],[4,5,6]])
print(a)
print(a.flatten(0,-1))
print(a.flatten(0,-2))
print(a.flatten(0))

输出如下:

1
2
3
4
5
6
tensor([[1., 2., 3.],
[4., 5., 6.]])
tensor([1., 2., 3., 4., 5., 6.])
tensor([[1., 2., 3.],
[4., 5., 6.]])
tensor([1., 2., 3., 4., 5., 6.])

4.2、reshape

用于改变维度,常用-1进行自动填充某个维度。tensor.reshape((0维度大小,1维度大小。。。))。其可以用于不连续空间的维度调整

1
2
3
4
5
6
7
8
a=torch.randn((2,3))
print(a)
print(a.reshape((3,2)))
tensor([[-0.4904, 0.7570, -0.5010],
[ 0.7374, 2.9273, -2.4853]])
tensor([[-0.4904, 0.7570],
[-0.5010, 0.7374],
[ 2.9273, -2.4853]])

4.3、Tensor.view

view也可以用于改变维度,和reshape类似,但是有区别,和reshape的区别是view要内存连续存储,reshape可以不连续需要注意的是,array的view是用来改变dtype和type的,用法不一样,可以用array.view?来查看函数的help。

1
2
b=torch.Tensor([[1,2,3],[4,5,6]])
print(b.view(-1))

输出如下:

1
2
3
4
tensor([1., 2., 3., 4., 5., 6.])
tensor([[1., 2.],
[3., 4.],
[5., 6.]])

4.3、tensor.expand 和 tensor.expanda_as

expand()函数的功能是用来扩展张量中某维数据的尺寸,它返回输入张量在某维扩展为更大尺寸后的张量。其扩展维度的本质是和广播机制一致(详见本文的广播机制),即维度从后往前和扩展成的维度对比,必须要完全一致,不一致的必须是该tensor原始维度为1(广播机制是任意一个为1即可)。值得注意的是,扩展张量不会分配新的内存,只是在存在的张量上创建一个新的视图,其原始tensor和处理后的tensor是共享内存的。关于视图和存储,详见本文的tensor存储和视图原理

使用如下:tensor.expand((第0维度大小,第1维度大小,第2维度大小。。。))例:

1
2
3
a=torch.Tensor([[1],[4]])
b=a.expand(2,3)
print(a,"\n",b)

结果如下:

1
2
3
4
tensor([[1.],
[4.]])
tensor([[1., 1., 1.],
[4., 4., 4.]])

在来看一下内存共享,证明通过expand得到的b只是一个新的视图,不是一个新的存储:

1
2
b[1,1]=5
print(a,"\n",b)

输出如下,可以看出b变了一个值,导致一行全变了,a也变了,可以看出b只是一个新视图,对应新的stride而已,而没有新的空间,如果需要新的空间,可以使用b=tensor.copy()得到的b就是新存储。

1
2
3
4
tensor([[1.],
[5.]])
tensor([[1., 1., 1.],
[5., 5., 5.]])

对于expand_as其实作用和expand是一致的,只是expand需要的参数直接是shape,而expand_as需要的参数直接是tensor变量,就可以将变量变为传入tensor维度一样了。例如下:

1
2
3
a=torch.Tensor([[1,2,3,4],[5,6,7,8]])
b=torch.Tensor([5,6,7,8])
print(b.expand_as(a))

输出如下:

1
2
tensor([[5., 6., 7., 8.],
[5., 6., 7., 8.]])

4.4 None—增加维度(可以用于np和tensor中,list不可以,list可以先转换在转回来)

None在i维度之后出现,其实就是给第i维度的元素增加一个维度,就是加个[]。在0维度上是None,就是给np数组总体多加个括号作为维度,比如a[:,None]就是在第0维度后加上一个维度,a[None]就是在最外面加上一个维度。。

如:

1
2
3
4
5
6
7
8
9
a=np.zeros((4))
b=np.ones((3))
c=np.ones((4,5))
a=a[:,None]
print(a)
b=b[None,:]
print(b)
c=c[:,None]
print(c)

得到的结果如下,在0维度后加None,其实就是给原来0维度的元素加个维度,在0维度之前加None,就总给np数组总体加个维度。注意:变量c有两个维度,所以中间加个None,应该是c[:,None,:],如果None后还有维度,但是我们没写,默认是:,如果写了,则服从切片原理。

1
2
3
4
5
6
7
8
9
10
11
12
[[0.]
[0.]
[0.]
[0.]]
[[1. 1. 1.]]
[[[1. 1. 1. 1. 1.]]

[[1. 1. 1. 1. 1.]]

[[1. 1. 1. 1. 1.]]

[[1. 1. 1. 1. 1.]]]

None除了增加维度外,还可以用于测试一个变量是否被定义,比如我们需要一个length变量来存储输入数据的长度,如果length没有定义,那么就丢出错误或者定义一个。例子如下:

1
2
3
4
5
6
7
8
9
10
11
if a is None:
print("a 不存在")
raise ValueError("a should not be None when box_predictor "
"is not specified")#圆括号隐式转换,加一个括号中,可以应对一行语句太长,分为多行。
else:
print("a 是存在的")
if length is None:
raise ValueError("length should not be None when box_predictor "
"is not specified")
else:
print("length 是存在的")

结果如下,a是存在的,所以执行的是else语句,而length是不存在的,所以我们认为抛出一个错误。

1
2
3
4
5
6
7
8
9
10
11
a 是存在的
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-21-e7b9afa88565> in <module>
5 else:
6 print("a 是存在的")
----> 7 if length is None:
8 raise ValueError("length should not be None when box_predictor "
9 "is not specified")#圆括号隐式转换,加一个括号中,可以应对一行语句太长,分为多行。

NameError: name 'length' is not defined

4.4.1、not

由于上面None说到了可以判别一个量是否被定义,这里就要说一下not,而not可以用于判断一个变量是否为空。首先看一下not的本质,其本质就是对布尔值进行取反,而空变量的布尔属性是False的(python中False和True首字母都大写),如下:

1
2
3
4
a=[]
print(bool(a))
b=[0,2,3]
print(bool(b))

结果如下,可以看出空变量的布尔属性是False,而非空变量的布尔属性是True。

1
2
False
True

所以可以通过如下语句判断一个量是否空的:

1
2
if not a:
print("a 是空的")

其中not的功能可以理解为将布尔值取反。

4.5、tensor.permute

permute有排序、置换的意思,tensor.permute(1,0)就是把原来维度1的数量放到维度0上,把原来维度0的数量放到维度1上。比如a.permute(3,0,2,1)就是把原本shape:[a,b,c,d]变成[d,a,c,b]。其是用于维度交换的。

测试如下:

1
2
3
4
5
a=torch.Tensor([[1,2,3],[4,5,6]])
b=a.permute(1,0)
print(b)
b[1,1]=0
print(b,"\n",a)

输出如下,可以看出,其实permute置换只是得到一个新的视图,而没有新的存储空间,其和a是共享的,这个也符合torch中节约空间和加速的出发点。可以用tensor.copy()开辟一段新存储空间。

1
2
3
4
5
6
7
8
tensor([[1., 4.],
[2., 5.],
[3., 6.]])
tensor([[1., 4.],
[2., 0.],
[3., 6.]])
tensor([[1., 2., 3.],
[4., 0., 6.]])

4、unsqueeze

可以增加一个维度,和stack比较类似,只是stack有一个拼接的过程。其增加维度方式和stack类似。在a=[[1,2,3],[4,5,6]]的时候,a[:,0]显然会造成降维,可以a[:,0].unsqueeze(1)这样就可以保持也是二维的,增加的维度是维度1,即在维度0后面增加,其实对于unsqueeze增加维度,传入参数是n,就是给原tensor的n-1后加个维度,也就是给n-1维度的元素加个维度。

例:

1
2
3
a=torch.randperm(10).reshape((2,5))
print(a)
print(a.unsqueeze(0),"\n",a.unsqueeze(1),"\n",a.unsqueeze(2))

输出如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
tensor([[5, 3, 2, 9, 1],
[4, 0, 7, 6, 8]])
tensor([[[5, 3, 2, 9, 1],
[4, 0, 7, 6, 8]]])
tensor([[[5, 3, 2, 9, 1]],

[[4, 0, 7, 6, 8]]])
tensor([[[5],
[3],
[2],
[9],
[1]],

[[4],
[0],
[7],
[6],
[8]]])

squeeze是去除维度的,其只能去除维度值为1的维度,如下:

1
print(a.unsqueeze(0).squeeze(0))

输出如下:

1
2
tensor([[5, 3, 2, 9, 1],
[4, 0, 7, 6, 8]])

5.排序函数torch.topk()

torch.topk(),如名字般,是为了求tensor的某个维度的前k大或前k小的值(还有index)。其具体用法如下

1
2
3
4
5
6
topk(input, k, dim=None, largest=True, sorted=True, *, out=None) -> (Tensor, LongTensor)
input--tensor数据
k--指定k值
dim--指定维度
largest--默认是 True,则从大到小排序,False则从小到大排序。
sorted--默认是 True,即返回是按照顺序排好的

例如下:

1
2
3
a=torch.Tensor([[1,2,3,4],[5,6,7,8]])
b=torch.topk(a,3,dim=1)
print(b)

输出结果如下:

1
2
3
4
5
torch.return_types.topk(
values=tensor([[4., 3., 2.],
[8., 7., 6.]]),
indices=tensor([[3, 2, 1],
[3, 2, 1]]))

6、四舍五入,小数取舍,上下限设置等等

torch变量的方法clamp,设置下限为0,小于0的自动设置为0,可以用于切片时,但由非法(负数)的情况。

1
matched_idxs.clamp(min=0)

round函数,使用:a.round(),a是torch变量,该函数是对a进行四舍五入。

还有floor,ceil

7、返回坐标信息torch.where和nonzero

其返回的是元组,例子如下

1
2
a=torch.tensor([1,2,3,4,5,0,1,0])
print(torch.where(torch.eq(a,0)))

得到如下结果,即元组内才是torch变量,torch.where(torch.eq(a,0))[0]才能得到torch.tensor([5,7])

1
(tensor([5, 7]),)

之所以这样是因为where返回的元组结果是这样的([第0维度坐标],[第一维度坐标],[第二维度坐标],)。所以只有一个维度的时候元组也是([第0维度坐标],)。所以需要片选0才能得到结果。

再例如这样

1
2
a=torch.tensor([[1,2,3,4,5,0,1,0]])
print(torch.where(torch.eq(a,0)))

结果就是这样了

1
(tensor([0, 0]), tensor([5, 7]))

这时候坐标每个维度是分开的,如果想组合到一起,可以通过torch.stack函数来进行。

8、相等np.equal(对应元素相同)和np.array_equal(完全相同)。torch.equal和np.equal一致。

9、打乱顺序,随机采样,torch.randperm()函数,通过打乱顺序,切片取前n个就可以当做是随机采样了。

10、tensor.max(dim=1)

比如a=torch.tensor([[1,8,3],[4,5,6]]),则a.max(dim=0)就是[4,8,6],dim=1则就是为[8,6]

11、meshgrid函数,可以简单理解为网格划线

20、isinstance(a,(list,tuple))这个就是判断是否是list或者tuple类型。非的时候,这样表示:not isinstance()

21、torch.full((100,),0)即是得到100个元素的矩阵,都填充为0

100、不要改动框架中# tpye的语句,因为#代表注释符,但如果# type就是代表类型说明符,随便改动就会导致模型错误。

101、arange,np.arange(100),生成0-99,np.arange(95,100),生成95-99

102、转置arrya.T。

103、语音处理相关库,图像处理相关库,文件读取相关库os

105、array[0,…]的三个省略号代表所有维度

106、range(start,end,stride)

只写一个数,默认start为0,stride为1

107、图像存储问题

用minist数据集的时候,读入的图片都是1*32*32的,直接变成3232的话,然后imshow(),或者imshow(img,cmap=’gray_r’)都是没有问题的,minist的像素数据很奇特,其中有负数,如果直接将该32\32的数据保存,得到的是一个近乎全黑的图(测试了一下,那些保存的时候都是简单的进行取整,然后保存,然而minist的数据很多负数,其他正数也很小,也就3这样,255才是白色,3可以认为是黑色了),但是为什么imshow却没问题呢,应该是imshow显示的时候,将负数默认为0了,然后最大值缩放到255,其他等比例放大(或者是总体加上了一个正数,使得最小的负数加上后恰好为0,然后等比例放缩),所以观测没问题,但是那些PIL等等函数保存成jpg却成了黑图的原因(PIL这些图看来是保存的时候简单取整,读入的时候却会自动缩放为0-255)。那么我们保存的时候,先将负数变为0,然后等比例放大。img*(img>0))*255/np.max(img),这样即可,值得注意的是,np中的sum和max无论输入多少维度的,得到的只有一个值。

注:array都代表np变量,即np.array();tensor都代表tensor变量,即torch.Tensor

np里面习惯用axis,比如array.max函数,split(array,下标的list or 等分,dim)函数。split没有array.split格式。np中有很多都没有array.函数的格式,比如array.append(array)就不可以,得np.append(array1,array2),而list却是可以的。

np中的sum和max无论输入多少维度的,得到的只有一个值。

而torch中喜欢用dim

函数+?可以得到函数介绍,比如torch.stack?,记得函数后面不要加括号。

verilog中负数不能比较大小,坑爹呀

json

文件读取

np.loadtxt(“a.txt”,dtype = np.complex128),这就是以复数的形式读取文件,否则默认形式是float,float是实数域的,自然不可以读复数域的。

库安装

指定镜像地址的命令如下

pip install -i 镜像地址 包名

例如: pip install -i https://pypi.tuna.tsinghua.edu.cn/simple numpy

国内镜像地址:

清华:https://pypi.tuna.tsinghua.edu.cn/simple

阿里云:http://mirrors.aliyun.com/pypi/simple/

中国科技大学 https://pypi.mirrors.ustc.edu.cn/simple/

gdal库的安装:

python gdal安装与简单使用

华为的mindspore框架安装:

1
2
conda create -n mindspore python=3.7.5
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple mindspore

之所以新建一个环境,是因为mindspore所需要的python环境是3.7及以上的,若3.7之下的版本,是装不成功的。

cv2安装:

1
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple opencv-python

远程服务器登录报错,账号创建问题:/usr/bin/xauth: error/timeout in locking authority file /home/gezhilai/.Xauthority

解决如下:

1
2
3
sudo mkdir /home/gezhilai
sudo chown gezhilai:gezhilai -R /home/gezhilai
sudo usermod -s /bin/bash gezhilai

参考链接:

(29条消息) Liunx创建新用户登录异常:/usr/bin/xauth: error/timeout in locking authority file /home/liuqidong/.Xauthority_dong_liuqi的博客-CSDN博客

anaconda中新建环境

命令如下:

conda create —name=labelme python=3.6

代表创建了一个新的环境,环境名称叫labelme,使用的是python版本是3.6.

可以通过conda env list查看我们所创建的所有环境,通过conda activate labelme来激活labelme环境。

若conda激活环境失败(很明显的就是都不在base环境中),Your shell has not been properly configured to use ‘conda activate’。出现此报错的原因是因为之前的虚拟环境没有退出source deactivate,所以需要重新进入虚拟环境: source activate

退出虚拟环境:source deactivateconda deactivate

参考链接如下:

conda激活环境失败

批量安装包

如果我们在git上需要下载一个代码,使用git clone即可把代码下载到服务器端。有些人会提供批量安装的包的txt文件,所以此时cd到该txt文件路径下:

进行如下即可

1
pip install -r a.txt

RuntimeError: CUDA error: no kernel image is available for execution on the device报错

torch运行时候出现了该错误,此时是由于torch的版本和cuda驱动的版本不兼容,导致torch无法使用cuda,从而无法使用GPU报错。以个人理解,torch之所以要和cuda匹配,是因为torch中一些语法使用了GPU时候,用来cuda语法编程,从而要和安装的cuda平台相匹配。(就像你用python2.0的编程,却用3.0的解释器,会出错,当然,也可能没有出错,毕竟每一代还是有很多内容相同),这里报错时候使用的torch是1.8.1,cuda是10.1的,网上没有查到1.8.1的对应版本,出问题了,我们将torch卸载进行重装。

1、首先如下命令,

1
nvidia-smi

结果第一行如下:

1
NVIDIA-SMI 455.23.04    Driver Version: 455.23.04    CUDA Version: 11.1   

可以看出,该服务器的RTX3090的最高支持驱动是11.1。

2、查看如今的CUDA驱动版本

可以使用

1
nvcc -V

1
cat /usr/local/cuda/version.txt

查看当前的CUDA驱动版本。

CUDA驱动略低于最高支持驱动是可以的。当然低太多就凉了。

3、知道如上结果,就可以按照第2步的CUDA驱动版本安装对应的torch了,其中安装的时候torch、torchvision、torchaudio、cudatoolkit需要对应。

这里需要说明一下cudatoolkit,cat /usr/local/cuda/version.txt得到的结果是平时我们所说的CUDA版本,其是在CUDA Toolkit工具包中,所以该CUDA版本也是CUDA Toolkit工具包的版本。

但是装了Anaconda之后Anaconda也会提供一个cudatoolkit工具包,同样包含了CUDA的运行API,可以用来替代官方CUDA的CUDA Toolkit。这也就是为什么有时候我们通过nvcc-V查看的cuda版本很低(比如7.5),但是能成功运行cuda9.0的pytorch的原因。因为在安装完anaconda后,运行pytorch代码就会使用anaconda的cudatoolkit,而忽视官方的CUDA Toolkit,所以我们只需要根据anaconda的cudatoolkit包的版本来安装相应的pytorch即可。

查看cudatoolkit直接

1
conda list

即可看到cudatoolkit版本,Wheel(即pip安装)的形式,可以看到torch版本是1.7.0+cu11.0的样式,其cu11.0就是anaconda的cudatoolkit,如果是conda安装的,那么可以直接看到cudatoolkit是作为一个库独立存在,可以看到其版本。

由此可知,我们完全可以在anaconda中相对应的cudatoolkit,具体的torch版本对应的torchvision、torchaudio和可选的cudatoolkit版本可以在如下官网查看。

torch、torchvision、python的版本对应

各个torch版本下及其对应的torchvision、cudatoolkit的安装命令

由于由第一步知道,cuda最高支持驱动为11.0,那么我们就选用11.0cudatoolkit下的某个toch版本,

在此选用了1.7.0,对应的torchvison可以查上面两个链接,使用wheel安装,命令如下

1
pip install torch==1.7.0+cu110 torchvision==0.8.0+cu110 torchaudio==0.7.0 -f https://download.pytorch.org/whl/torch_stable.html

即安装的cudatoolkit是11.0的,torchvision是其对应版本。

至此安装后即可正常运行。

注:有的时候命令行操作python a.py可以,但是vscode端运行不可以,是不是因为调用了CUDATOOLKIT,而不是anaconda中的cudatoolkit,导致版本不兼容?

命令行运行py文件

正常如果不需要配置什么内容的话,直接如下即可运行:

1
python a.py

但如果有如下这一类的配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
def get_parser():
"""Get default arguments."""
parser = configargparse.ArgumentParser(
description="Transfer learning config parser",
config_file_parser_class=configargparse.YAMLConfigFileParser,
formatter_class=configargparse.ArgumentDefaultsHelpFormatter,
)
# general configuration
parser.add("--config", is_config_file=True, help="config file path")
parser.add("--seed", type=int, default=0)
parser.add_argument('--num_workers', type=int, default=0)

# network related
parser.add_argument('--backbone', type=str, default='resnet50')
parser.add_argument('--use_bottleneck', type=str2bool, default=True)

# data loading related
parser.add_argument('--data_dir', type=str, required=True)
parser.add_argument('--src_domain', type=str, required=True)
parser.add_argument('--tgt_domain', type=str, required=True)

# training related
parser.add_argument('--batch_size', type=int, default=32)
parser.add_argument('--n_epoch', type=int, default=100)
parser.add_argument('--early_stop', type=int, default=0, help="Early stopping")
parser.add_argument('--epoch_based_training', type=str2bool, default=False, help="Epoch-based training / Iteration-based training")
parser.add_argument("--n_iter_per_epoch", type=int, default=20, help="Used in Iteration-based training")

# optimizer related
parser.add_argument('--lr', type=float, default=1e-3)
parser.add_argument('--momentum', type=float, default=0.9)
parser.add_argument('--weight_decay', type=float, default=5e-4)

# learning rate scheduler related
parser.add_argument('--lr_gamma', type=float, default=0.0003)
parser.add_argument('--lr_decay', type=float, default=0.75)
parser.add_argument('--lr_scheduler', type=str2bool, default=True)

# transfer related
parser.add_argument('--transfer_loss_weight', type=float, default=10)
parser.add_argument('--transfer_loss', type=str, default='mmd')
return parser

def main():
parser = get_parser()

就需要额外的配置了:比如如下,上述中无default的,require为true的都要配置。

1
python main.py --config DAN/DAN.yaml --data_dir /remote-home/lwq/transferlearning/code/DeepDA/data18 --src_domain train18 --tgt_domain test18

在jupyter中使用虚拟环境

创建好新的环境后,如果使用jupyter时想用该环境的话,还需要配置一下:

首先查看当前的所有环境:

1
conda env list

输出如下:

1
2
3
4
5
6
7
8
9
10
11
12
# conda environments:
#
base * D:\anzhuang\anaconda
gf D:\anzhuang\anaconda\envs\gf
kite D:\anzhuang\anaconda\envs\kite
labelme D:\anzhuang\anaconda\envs\labelme
mindspore D:\anzhuang\anaconda\envs\mindspore
new_env D:\anzhuang\anaconda\envs\new_env
pycharm D:\anzhuang\anaconda\envs\pycharm
pythonProject1 D:\anzhuang\anaconda\envs\pythonProject1
pythonProject2 D:\anzhuang\anaconda\envs\pythonProject2
tensorflow D:\anzhuang\anaconda\envs\tensorflow

如果我们想在jupyter中使用gf这个环境的话,需要如下操作:

首先激活该环境:

1
conda activate gf

然后安装ipykernel

1
conda install ipykernel

最后添加对应文件:

1
python -m ipykernel install --name gf

此时已完成新环境的添加,如下打开jupyter即可:

1
jupyter notebook

注意:创建新环境的时候,激活后,使用jupyter notebook可能提示没有jupyter,那是没有安装jupyter,安装一下即可:

1
conda install jupyter

在jupyter中某个环境kernel error

Jupyter notebook中报错,出现kernel error解决方法

其中jupyter kernelspec list可以查看所有的环境用的kernel路径(json中配置了python编译器位置)

Pycharm中使用anconda中的环境

file-setting-Project Interpreter-system Interperter处,将python编译器变成anconda的,如果anaconda已加入环境变量中,是可以自动识别出来的

本地打开服务器的tensorboard

tensorboard —logdir=. —bind_all然后ip:6006就可以在本地打开了

Torch中使用GPU

代码中,常会发现如下两句:

1
2
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
model.to(device)

上述的”cuda:0”,如果电脑只有单张独立显卡,就是”cuda”。这两行是放在读取数据之前,意思是将模型复制一份到device上,如果GPU存在(同时torch是GPU版本,cudatooltik也匹配),torch.cuda.is_available()就会判断成立。然后后面读取数据的时候也通过data.to(device)把数据复制一份到GPU上,初始数据复制到了GPU上,中间的数据自然就会存在GPU的显存中,torch类方法中就有这个to方法。

而数据如下即可,g.cuda()如果之前没有特地的设置的,默认是cuda:0,所以如下即可。

1
2
3
if torch.cuda.is_available():
h = g.cuda()
print(h)

若是多块GPU,模型用到GPU上如下:

1
2
3
4
5
6
model = Model()
if torch.cuda.device_count() > 1:
model = nn.DataParallel(model,device_ids=[0,1,2])
elif:
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
model.to(device)

或者如下:(推荐)

1
2
os.environ['CUDA_VISIBLE_DEVICES'] = '0,3'
net = torch.nn.DataParallel(model)

数据加载的话,需要如下,但是.cuda默认是第一章卡,所以还需要os.environ[‘CUDA_VISIBLE_DEVICES’] = ‘0,3’这个命令在之前才可以。

1
2
inputs = inputs.cuda()
labels = labels.cuda()

如果多GPU下只用一张卡,还可以如下(不推荐),如下情况,虽然只指定了一张卡,但是print的结果不变,即不会改变可见显卡,后续还可以用torch.nn.DataParallel(model, device_ids=[1, 2])进行指定,但是必须包含set_device(1)指定的device:1的设备,缺点是仍然会存在占用一些device:0的gpu内存;

1
2
torch.cuda.set_device(1)
print(torch.cuda.device_count()) #可用GPU数量

当然可以通过其他方式制定GPU:

1
2
3
4
5
6
7
8
9
10
#(1)直接终端中设定 
CUDA_VISIBLE_DEVICES=1

#(2)python代码中设定:
import os
os.environ['CUDA_VISIBLE_DEVICE']='1'

#(3)使用函数set_device
import torch
torch.cuda.set_device(id)

把Tensor放到GPU上的方法如下,也可以g.to(device)。

1
2
3
if torch.cuda.is_available():
h = g.cuda()
print(h)

把GPU上的Tensor或者Variable的数据返回到CPU上变成numpy格式(比如绘图的时候需要用到,GPU上的数据没法直接在界面上可视化):

1
2
3
4
5
6
7
import torch
from torch.autograd import Variable

# 将变量或者数据移到GPU
gpu_info = Variable(torch.randn(3,3)).cuda()
# 将变量或者数据移到CPU
cpu_info = gpu_info.cpu().numpy()

注:常用的torch.cuda函数:

1
2
3
4
5
torch.cuda.is_available()#如果可以cuda驱动gpu,则为true
torch.cuda.device_count()#返回GPU可用数量,2则代表2个
torch.cuda.get_device_name()#返回GPU型号,如GeForce RTX 3090
torch.cuda.get_device_properties("cuda:0")#返回GPU性能,显存、线程等等
torch.cuda.current_device()#查看当前GPU使用序号。注意#os.environ["CUDA_VISIBLE_DEVICES"] = '1,2'这种指令会改变torch感知设备的编号,使用了1和#2后,torch.cuda.current_device()感知只会是0或1,因为感知是从0开始的。

参考链接:(29条消息) Pytorch to(device)_shaopeng568的专栏-CSDN博客

(29条消息) pytorch之多GPU使用——#CUDA_VISIBLE_DEVICES使用 #torch.nn.DataParallel() #报错解决_夏普通-CSDN博客

pytorch多gpu并行训练 - 知乎 (zhihu.com)

模型训练中显示启动卷积失败,cuda等等问题。

os.environ[‘CUDA_VISIBLE_DEVICES’] = ‘/gpu:0’

有一次写代码发现明明写的是:

os.environ[“CUDA_VISIBLE_DEVICES”] = “0,1,2,3,4,5,6,7”

但是却显示只发现了 gpu:0,百思不得其解,百度谷歌都没有发现类似问题,后来检查才发现有一个调用的py文件里写了一句:

os.environ[“CUDA_VISIBLE_DEVICES”] = “0”

删掉之后,可以发现全部的gpu

tf导包失败问题

tf中,如下导包失败,报错没np_utils这个包,一般这种问题很有可能是版本问题导致包已经到其他仓库里了或者修改了包名等等。这里其实是keras在版本迁移的时候,包的位置迁移了导致导不进来,在__init__.py可以看到。

1
from tensorflow.keras.utils import np_utils

修改成如下,即多了个python位置,此时导入不报错,但是使用的时候有问题即np_utils.方法都不能用。

1
import tensorflow.python.keras.utils.np_utils

如下即可成功导入并使用

1
from tensorflow.python.keras.utils import np_utils

参考:(29条消息) 解决ImportError: cannot import name ‘np_utils‘ from ‘tensorflow.keras.utils‘ 的问题_小了白了兔DY的博客-CSDN博客

学习网站

缪雪峰的官方网站

- - - - - - - - - - - - - - 本文结束啦,感谢您的观看 - - - - - - - - - - - - - -