当前位置: 首页 > news >正文

RCNN模型Python实现笔记一

文章目录

    • 一、相关函数讲解
      • 1. `log_softmax()`函数
      • 2. `contiguous()`函数
      • 3. `view()`函数
      • 4. `cv2.cvtColor()`函数
      • 5. `Variable()`函数
      • 6. `torch.IntTensor()`函数
      • 7. `readlines()`函数
    • 二、疑难代码行
      • `preds = preds.transpose( 1, 0 ).contiguous().view( -1 )`
      • `preds_size = Variable( torch.IntTensor( [preds.size( 0 )] ) )`
      • `image = Image.fromarray(np.uint8(img)).convert('L')`
      • `image = Variable( image )`
      • `if gpu: model.load_state_dict( torch.load( model_path ) )`
      • `wrong_results.append('res:{} / label:{}'.format(res,label))`
    • 三、附录 recognizer.py代码

本博客为学习RCNN的代码实现做的笔记,仅作个人参考

一、相关函数讲解

1. 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。

输入张量需要满足一定的条件,需要是可导的,且为浮点型或双精度型。



2. contiguous()函数

contiguous()是Pytorch中的一个函数,它用来确保一个张量在内存中是连续的,返回一个内存连续的新的张量,并且不改变原始张量的值。当在处理某些需要内存连续性的操作时需要使用这个函数,如torch.is_tensor()torch.tensor()torch.storage()等。



3. 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,后面三维分别对应图像的长、宽、高。



4. cv2.cvtColor()函数

cv2.cvtColor()是OpenCV中的一个函数,它可以将一幅图像从一种颜色空间转换为另一种颜色空间。该函数的第一个参数是要转换的图像,第二个参数是当前图像的颜色空间,第三个参数是目标颜色空间。例如,可以使用cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)将一幅BGR图像转换为灰度图像。



5. Variable()函数

torch.autograd.Variable(data, requires_grad=True, grad_fn=None)

其中,data表示要封装的张量, requires_grad表示是否需要求梯度, grad_fn表示是由哪个函数生成的。它接收一个Tensor张量作为输入,并返回一个新的变量。这个变量是torch.autograd.Variable类型的。



6. 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等, 这些张量类型可以被用在不同场景中。



7. 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进行三步操作:

  1. 首先使用transpose(1, 0)函数对preds进行矩阵转置,交换矩阵的行和列。
  2. 然后使用contiguous()函数将转置后的张量转化为内存连续的张量。
  3. 最后使用view(-1)函数对张量进行尺寸修改,将它转换为一个一维张量,其中包含与原始张量相同的元素。

在这里,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中用来跟踪和计算梯度的类,它包含了三个重要的属性:

  • data : 存储了Variable所包含的数据的Tensor张量
  • grad : 存储了反向传播过程中用来计算梯度的张量
  • grad_fn : 存储了创建当前Variable的函数,用来进行反向传播。这个类主要用来在计算图中进行反向传播,计算梯度等操作,在训练神经网络时是一个非常重要的类。

这个操作通常在训练神经网络之前需要将数据转换成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.

这样做的好处是可以方便地更换变量值,并且可以清晰地看出变量的位置。



三、附录 recognizer.py代码

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 数组中。

这样就可以方便的查看哪些结果是错误的。
'''



相关文章:

  • STC15系列单片机EEPROM读写示例
  • [GYCTF2020]Blacklist(堆叠注入)
  • SM4算法学习笔记
  • NISP证书的含金量有多高,是否能够实现2023年月薪过万呢?
  • [Android]动画
  • 【JavaGuide面试总结】计算机网络·上
  • 【Linux】两个故事带你使用git命令行
  • 循环语句(循环结构)——“C”
  • 初识 Django(Python WEB 框架)
  • 用PYTHON自动登录SAP GUI
  • 网络工程师必须搞清楚MPLS与专线的区别
  • 【Linux】Linux软件包管理器与Linux编辑器
  • 数据库系统概念 | 第三章:SQL介绍
  • 算法动态规划 0-1背包
  • 高可用是指什么意思
  • PHP MySQL Order By 关键词
  • HTML常见转义字符
  • 【C++】面向对象---多态(万字详解)
  • 活动星投票感动年度十大人物网络评选微信的投票方式线上免费投票
  • 秒懂 Java ThreadLocalRandom