log_softmax()
函数torch.nn.functional.log_softmax()
是PyTorch中用来计算输入张量的log_softmax操作的函数。
log_softmax函数的计算公式为:
l o g _ s o f t m a x ( x i ) = l o g ( e x i ∑ j = 1 N e x j ) log\_softmax(x_i) = log(\frac{e^{x_i}}{\sum_{j=1}^Ne^{x_j}}) log_softmax(xi)=log(∑j=1Nexjexi)
其中,x是输入的张量, x i x_i xi表示x中第i个元素,N表示x的元素个数。Log_Softmax 函数的值域是 ( − ∞ , 0 ] (-\infty,0] (−∞,0]
使用log_softmax
函数可以得到更好的预测结果,因为它使预测结果归一化,每个元素都是在对数空间上的概率值。
这个函数是在torch.nn.functional
中的,可以直接使用F.log_softmax()
或torch.nn.functional.log_softmax()
来调用,可以使用的参数就是输入的张量,以及指定对哪一维进行softmax。
输入张量需要满足一定的条件,需要是可导的,且为浮点型或双精度型。
contiguous()
函数contiguous()
是Pytorch中的一个函数,它用来确保一个张量在内存中是连续的,返回一个内存连续的新的张量,并且不改变原始张量的值。当在处理某些需要内存连续性的操作时需要使用这个函数,如torch.is_tensor()
,torch.tensor()
,torch.storage()
等。
view()
函数view()
是Pytorch中的一个函数,它用来对图像的尺寸进行修改。它接收一个尺寸参数作为输入,该参数指定了新图像的尺寸。返回一个新的张量,其尺寸与给定尺寸相同,并且包含与原始张量相同的元素。这个函数并不改变原始张量,而是返回一个新的张量。
例如:
x = torch.randn(4, 4)
y = x.view(16)
z = x.view(-1, 8)
print(x.size()) # torch.Size([4, 4])
print(y.size()) # torch.Size([16])
print(z.size()) # torch.Size([2, 8])
在这个例子中, x是一个 4 × 4 4\times4 4×4的张量,使用view(16)函数后返回了一个 1 × 16 1\times16 1×16的张量y, view(-1, 8)返回了一个 2 × 8 2\times8 2×8的张量z。
例如:
image = image.view( 1, *image.size() )
这一行代码使用了Pytorch中的view()
函数来对图像进行尺寸修改。
其中,*image.size()
是一个拆包操作,它将image.size()
的所有元素拆开并传入view()函数中。
view(1, *image.size())
将图像的维度扩展为1 + image.size()的维度。这意味着它会在图像的第一维上增加一个维度,并将原来的图像的尺寸保留下来。
x = torch.randn(3, 4, 5)
y = x.view(1, *x.size())
print(x.size()) # torch.Size([3, 4, 5])
print(y.size()) # torch.Size([1, 3, 4, 5])
在这个例子中,x是一个3x4x5的张量,使用view(1, *x.size())
函数后返回了一个 1 × 3 × 4 × 5 1\times3\times4\times5 1×3×4×5的张量y.
这段代码通常用在将图像转换为4D张量并传入神经网络中进行处理。因为神经网络中的输入一般是4维的,所以将图像转换为4维张量是很常见的操作。
如果原始图像是一个3维张量,使用view(1, *x.size())
函数可以将其转化为4维的张量,第一维是batch_size,后面三维分别对应图像的长、宽、高。
cv2.cvtColor()
函数cv2.cvtColor()
是OpenCV中的一个函数,它可以将一幅图像从一种颜色空间转换为另一种颜色空间。该函数的第一个参数是要转换的图像,第二个参数是当前图像的颜色空间,第三个参数是目标颜色空间。例如,可以使用cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
将一幅BGR图像转换为灰度图像。
Variable()
函数torch.autograd.Variable(data, requires_grad=True, grad_fn=None)
其中,data
表示要封装的张量, requires_grad
表示是否需要求梯度, grad_fn
表示是由哪个函数生成的。它接收一个Tensor张量作为输入,并返回一个新的变量。这个变量是torch.autograd.Variable
类型的。
torch.IntTensor()
函数torch.IntTensor()
是Pytorch中用来创建整型张量的函数。它可以接收一个列表、元组、矩阵等形式的数据作为输入,并返回一个整型张量。
例如:
x = torch.IntTensor([1, 2, 3, 4]) # 创建一个1维整型张量
y = torch.IntTensor([[1, 2], [3, 4]]) # 创建一个2维整型张量
也可以使用numpy数组或者其它张量来创建新的张量
import numpy as np
a = np.array([1, 2, 3, 4])
x = torch.IntTensor(a)
这个函数常用于数据预处理,在训练模型前将数据转换成张量的形式,这样才能输入到模型中进行训练。
注意: Pytorch 中有多种不同类型的张量,如
torch.FloatTensor
,torch.DoubleTensor
等, 这些张量类型可以被用在不同场景中。
readlines()
函数readlines()
是Python中文件操作的一个函数,它用于读取文件中的所有行并返回一个列表。该列表中的每个元素都是文件中的一行。
语法:
file_object.readlines()
其中,file_object
是一个文件对象。使用 open()
函数打开文件后,可以得到一个文件对象。
示例:
with open('test.txt', 'r') as f:
lines = f.readlines()
for line in lines:
print(line)
在这个例子中,readlines()
函数读取了文件test.txt中的所有行,并将它们存储在lines列表中,最后通过循环打印每一行
注意,使用 readlines() 函数读取文件时,整个文件都会被读取到内存中,如果文件非常大,可能会导致内存不足的问题。
preds = preds.transpose( 1, 0 ).contiguous().view( -1 )
这一行代码是在对一个张量preds进行三步操作:
在这里,
transpose(1, 0)
函数会将维度1和维度0交换,也就是说将原来的第一维和第二维交换。如果原始张量是一个二维矩阵,那么这个函数就是将这个矩阵的行和列交换。
这些操作的结果是将preds张量转化为一个一维的内存连续的张量,其中包含与原始张量相同的元素。
preds_size = Variable( torch.IntTensor( [preds.size( 0 )] ) )
上面这行代码中, torch.IntTensor([preds.size(0)])
创建了一个整型的张量,并将preds
的第一维(size(0))
的长度作为其值,然后这个张量被包装成了一个Variable
类型的变量preds_size
, 这个变量存储了preds
张量第一维的长度,而非张量。
image = Image.fromarray(np.uint8(img)).convert('L')
这句代码使用了 PIL (Python Imaging Library) 库中的Image.fromarray
函数将 numpy 数组转换为 PIL 图像,并使用.convert('L')
将其转换为灰度图。
np.uint8(img)
这句话是将numpy数组转化为8位无符号整型,因为PIL库中Image.fromarray()
函数只接受uint8类型数组作为参数。
转换为灰度图的原因是灰度图只有一个通道,处理起来更简单,并且对于文本识别来说,灰度图也足够用了。
image = Variable( image )
这一行代码使用了Pytorch中的Variable()
函数来将一个张量转换成变量类型。
这里的image
是一个张量,使用 Variable(image) 函数将其转换成torch.autograd.Variable
类型的变量。
torch.autograd.Variable是Pytorch中用来跟踪和计算梯度的类,它包含了三个重要的属性:
这个操作通常在训练神经网络之前需要将数据转换成Variable类型。
注意: Pytorch已经将Variable类合并到了Tensor类中,因此在最新版本中不需要使用Variable类型。
if gpu: model.load_state_dict( torch.load( model_path ) )
这一段代码用来检查是否使用GPU,并在使用GPU时加载模型参数。
其中,变量 gpu 是一个布尔值,如果它为True,则表示当前程序正在使用GPU。
如果gpu为True,则执行以下操作:
torch.load()
函数加载模型参数,该函数读取指定路径的文件并返回一个字典,字典中包含了模型的参数。model.load_state_dict()
函数将加载的模型参数加载到模型中。如果gpu为False,则表示不使用gpu进行训练或者推理操作。
这样做的好处是,如果计算资源有限,或者没有GPU可用,可以在CPU上进行推理,而不需要修改代码。
同时,这种方式也可以在有GPU的情况下进行更快的推理,而不需要修改代码。
总之,这段代码提供了一种灵活的方式来选择是否使用GPU进行推理,并且可以在不修改代码的情况下切换使用GPU或者CPU。
wrong_results.append('res:{} / label:{}'.format(res,label))
.append('res:{} / label:{}'.format(res,label))
表示将字符串 'res:{} / label:{}'
格式化,并将其添加到 wrong_results
数组中。
其中 'res:{} / label:{}'
中的 {} 是占位符,它会被 format()
函数中传入的参数替换。
即将识别结果(res)和真实标签(label)的值插入到字符串 'res:{} / label:{}'中,并添加到 wrong_results 数组中,方便后续处理。
例如: 假设 res 的值为 “apple”,label 的值为 “banana”,那么这句话将会把 'res:apple / label:banana'
添加到 wrong_results 数组中。
这样就可以方便的查看哪些结果是错误的。这段代码通常用在错误检测和错误分析中,方便追踪错误预测的结果和正确结果。
为什么这里要用fomat函数?
'res:{} / label:{}'.format(res,label)
使用了字符串格式化函数format()
,它可以将变量的值插入到字符串中。
{}
是占位符,表示要插入的变量的位置。在这里 {}
被 res 和 label的值替换了。
使用 format()
函数比直接将变量拼接到字符串中更加灵活和易于维护,并且更加容易阅读。
例如:
name = 'Alice'
age = 25
print('My name is {} and I am {} years old.'.format(name, age))
输出:
My name is Alice and I am 25 years old.
这样做的好处是可以方便地更换变量值,并且可以清晰地看出变量的位置。
import torch
from torch.autograd import Variable
import utils
import mydataset
from PIL import Image
import numpy as np
import crnn as crnn
import cv2
import torch.nn.functional as F
import keys
import config
gpu = True
if not torch.cuda.is_available():
gpu = False
model_path = './crnn_models/CRNN-0618-10w_21_990.pth'
alphabet = keys.alphabet
print(len(alphabet))
imgH = config.imgH
imgW = config.imgW
model = crnn.CRNN(imgH, 1, len(alphabet) + 1, 256)
if gpu:
model = model.cuda()
print('loading pretrained model from %s' % model_path)
if gpu:
model.load_state_dict( torch.load( model_path ) )
else:
model.load_state_dict(torch.load(model_path,map_location=lambda storage,loc:storage))
converter = utils.strLabelConverter(alphabet)
transformer = mydataset.resizeNormalize((imgW, imgH),is_test=True)
'''
cv2.cvtColor()是OpenCV中的一个函数,它可以将一幅图像从一种颜色空间转换为另一种颜色空间。该函数的第一个参数是要转换的图像,
第二个参数是当前图像的颜色空间,第三个参数是目标颜色空间。例如,可以使用cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)将一幅BGR图像转换为灰度图像。
'''
def recognize_downline(img,crnn_model=model):
img = cv2.cvtColor( img, cv2.COLOR_BGR2RGB )
image = Image.fromarray(np.uint8(img)).convert('L')
image = transformer( image )
if gpu:
image = image.cuda()#检查 gpu 的值是否为真,如果为真则将 image 移动到 GPU 上
image = image.view( 1, *image.size() )#使用 image = image.view( 1, *image.size() ) 将图像的大小调整为适合模型的大小
image = Variable( image )#使用 image = Variable( image ) 将图像转换为 PyTorch 变量
model.eval()#使用 model.eval() 将模型设置为评估模式
preds = model( image )
preds = F.log_softmax(preds,2)
conf, preds = preds.max( 2 )
preds = preds.transpose( 1, 0 ).contiguous().view( -1 )
preds_size = Variable( torch.IntTensor( [preds.size( 0 )] ) )
raw_pred = converter.decode( preds.data, preds_size.data, raw=True )
sim_pred = converter.decode( preds.data, preds_size.data, raw=False )
return sim_pred.upper()
'''
#首先使用 img = cv2.cvtColor( img, cv2.COLOR_BGR2RGB ) 将图像的颜色空间从 BGR 转换为 RGB
torch.autograd.Variable(data, requires_grad=True, grad_fn=None)
其中,data表示要封装的张量, requires_grad表示是否需要求梯度, grad_fn表示是由哪个函数生成的。
使用Variable类型的对象时,我们可以将其当做普通张量一样进行操作,如加减乘除、矩阵乘法等。对其进行运算时PyTorch会自动构建计算图,并在反向传播时计算梯度。
例如,在上面的代码中,这一句 image = Variable( image ) 会将 image 转换成一个 Variable 类型的对象,表示这个张量需要进行求导。然后就可以使用这个Variable类型的对象进行后续的计算。
总的来说,Variable是Pytorch中用来表示张量的类,它封装了一个张量并且为其提供自动求导功能,在使用PyTorch进行深度学习时,经常会用到Variable来封装数据。
image = Image.fromarray(np.uint8(img)).convert('L') 这句代码使用了 PIL (Python Imaging Library) 库中的 Image.fromarray 函数将 numpy 数组转换为 PIL 图像,并使用 .convert('L') 将其转换为灰度图。
np.uint8(img) 这句话是将numpy数组转化为8位无符号整型,因为PIL库中Image.fromarray()函数只接受uint8类型数组作为参数。
转换为灰度图的原因是灰度图只有一个通道,处理起来更简单,并且对于文本识别来说,灰度图也足够用了。
'''
if __name__ == '__main__':
import shutil
saved_path = 'test_imgs/'
wrong_results = list()
with open('data_set/infofile_test.txt') as f:#使用 f.readlines() 读取文件中所有行,并将结果赋值给变量 content
content = f.readlines()#
num_all = 0
num_correct = 0
for line in content:#使用 for line in content 循环遍历每一行,对每一行进行处理。
fname, label = line.split('g:')#使用 line.split('g:') 将每一行按照字符 'g:' 分割成两部分,分别赋值给变量 fname 和 label。
fname += 'g'
label = label.replace('\r', '').replace('\n', '')#使用 label.replace('\r', '').replace('\n', '') 将 label 中的字符 '\r' 和 '\n' 替换为空字符
img = cv2.imread(fname)
res = recognize_downline(img)
if res==label:
num_correct+=1
else:
# new_name = saved_path + fname.split('/')[-1]
# shutil.copyfile(fname, new_name)
wrong_results.append('res:{} / label:{}'.format(res,label))
num_all+=1
print(fname,res==label,res,label)
print(num_correct/num_all)
# print(wrong_results)
'''
.append('res:{} / label:{}'.format(res,label)) 表示将字符串 'res:{} / label:{}' 格式化,并将其添加到 wrong_results 数组中。
其中 'res:{} / label:{}' 中的 {} 是占位符,它会被 format() 函数中传入的参数替换。
即将识别结果(res)和真实标签(label)的值插入到字符串 'res:{} / label:{}'中,并添加到 wrong_results 数组中,方便后续处理。
例如: 假设 res 的值为 "apple",label 的值为 "banana",那么这句话将会把 'res:apple / label:banana' 添加到 wrong_results 数组中。
这样就可以方便的查看哪些结果是错误的。
'''