pytroch学习笔记三————张量(具体运用)
处理图像
有几种方法可以将颜色变为数字,最常见的方法是RGB,颜色由三个数字定义,分别代表红绿蓝的强度
加载图像文件(imageio模块)
import imageio
img_arr = imageio.imread('img/bobby.jpg')
print(img_arr.shape)
# (720,1280,3)
此时img是一个具有3个维度的类numpy数组对象,2个空间维度:高度和宽度,以及第三个维度对应红绿蓝,但是处理图像数据的pytorch需要的张量排列是 C × H × W C\times H\times W C×H×W(分别表示通道,高度,宽度)
改变布局
运用permute()
方法使每一个新的维度得到一个合适的布局
A=T.permute(a1,a2,...,an)
an的位置为n,这个语句代表着新生成的张量A在n位置上的维度对应着原始张量T在an位置上的维度
img=torch.from_numpy(img_arr)
out=img.permute(2,0,1)
到目前为止我们只描述了单幅图像,但我们需要多幅图像,所以我们可以预分配一个适当大小的张量并使用从目录中加载的图像填充它
所以变成了四个维度:
N
×
C
×
H
×
W
N\times C\times H\times W
N×C×H×W
batch_size = 3
batch = torch.zeros(batch_size, 3, 256, 256, dtype=torch.uint8)
import os
data_dir = 'filepath'
filenames = [name for name in os.listdir(data_dir)
if os.path.splitext(name)[-1] == '.png']
for i, filename in enumerate(filenames):
img_arr = imageio.imread(os.path.join(data_dir, filename))
img_t = torch.from_numpy(img_arr)
img_t = img_t.permute(2, 0, 1)
img_t = img_t[:3] # 只保留三个通道,因为还有第4个通道代表着透明度
batch[i] = img_t
正规化数据
为了能够使神经网络顺利的对图像进行处理,所以需要将数据转化成[0,1]或[-1,1]之间的数据,第一种做法可以将数据类型变为float后将像素值除255,第二种是计算输入数据的均值和标准差,并对其进行缩放,使每个通道的均值为0,标准差为1
第二种做法如下:
n_channels = batch.shape[1]
for c in range(n_channels):
mean = torch.mean(batch[:, c])
std = torch.std(batch[:, c])
batch[:, c] = (batch[:, c] - mean) / std
三维图像:体数据
类似于二维图像,加载三维图像仅需要多加载一个深度维度,因此标准的三维图像的维度为:
N
×
C
×
D
×
H
×
W
N\times C\times D\times H\times W
N×C×D×H×W,分别对应图片数量,通道,深度,高度,宽度
利用imageio模块中的volread()
函数加载一个CT扫描样本,该函数接受将目录作为参数,并将所有医学数字成像和通信(Digital Imaging and Communication in Medicine, DICOM)文件汇编为一个Numpy三维数组
加载特定格式的医学图像如下:
import numpy as np
import torch
import imageio
dir_path = "filepath/2-LUNG 3.0 B70f-04083"
vol_arr = imageio.volread(dir_path, 'DICOM')
由于没有通道信息,布局与pytorch期望不同,所以要用unsqueeze()
为通道维度留出空间
vol = torch.from_numpy(vol_arr).float()
vol = torch.unsqueeze(vol, 0)
表示表格数据
假定样本在表中的出现顺序没有意义,这样的表是独立样本的集合,不像时间序列那样
加载数据张量
import numpy as np
import torch
torch.set_printoptions(edgeitems=2, precision=2, linewidth=75)
import csv
wine_path = "filepath/winequality-white.csv"
wineq_numpy = np.loadtxt(wine_path, dtype=np.float32, delimiter=";",
skiprows=1)
col_list = next(csv.reader(open(wine_path), delimiter=';'))
wineq = torch.from_numpy(wineq_numpy)
当我们试图理解数据时,应该意识到有3种不同的数值,分别是:
- 连续值:用数字表示是最直观的,他们是严格有序的
- 序数值:对连续值的严格排序仍然存在,但是值之间的固定关系不再适用
- 分类值:其值没有排序意义,也没有数字意义
表示分数
将除最后一列的数据变为输入值,将最后一列的数据变成输出值
data = wineq[:, :-1]
target = wineq[:, -1]
如果想将target张量变成标签张量,有2种方法
一种是简单直接将标签视为分数的整数向量,如果目标张量是字符串标签,如葡萄酒的颜色,那么为每一个字符分配一个整数将允许我们采用相同的方法
另一种则是构建分数的一个独热编码
独热编码
将10个分数编码到一个由10个元素组成的向量中,除了其中一个元素设置为1,其他的都设置为0
如果分数是完全分散的,没有隐含的顺序和距离,则独热编码更加的合适
利用scatter_()
方法可获得一个独热编码,该方法将沿着参数提供的索引方向将源张量的值填充到输入张量中
scatter_()
参数解释:因为是以下划线结尾,所以是直接修改张量
- 指定一下两个参数的维度
- 一个列张量,表示要散射的元素的索引张量
- 包含要散射元素的张量,或要散射的单个标量
target_onehot = torch.zeros(target.shape[0], 10)
target_onehot.scatter_(1, target.unsqueeze(1), 1.0)
target_unsqueezed = target.unsqueeze(1)
数据归一化
data_mean = torch.mean(data, dim=0)
data_var = torch.var(data, dim=0)
data_normalized = (data - data_mean) / torch.sqrt(data_var)
寻找阈值
利用pytorch中的高级索引的功能,可以使用数据类型torch.bool
来索引data(过滤data),使其包含索引张量中与True对应的项或行
bad_indexes = target <= 3
bad_data = data[bad_indexes]
把葡萄酒分为好酒,中等酒和劣质酒3类,
bad_data = data[target <= 3]
mid_data = data[(target > 3) & (target < 7)]
good_data = data[target >= 7]
bad_mean = torch.mean(bad_data, dim=0)
mid_mean = torch.mean(mid_data, dim=0)
good_mean = torch.mean(good_data, dim=0)
for i, args in enumerate(zip(col_list, bad_mean, mid_mean, good_mean)):
print('{:2} {:20} {:6.2f} {:6.2f} {:6.2f}'.format(i, *args))
乍一看劣质葡萄酒的二氧化硫含量跟高,可因此把二氧化硫作为分好酒劣质酒的标准
torch.lt
是比较规范的小于
total_sulfur_threshold = 141.83
total_sulfur_data = data[:,6]
predicted_indexes = torch.lt(total_sulfur_data, total_sulfur_threshold)
actual_indexes = target > 5
n_matches = torch.sum(actual_indexes & predicted_indexes).item()
n_predicted = torch.sum(predicted_indexes).item()
n_actual = torch.sum(actual_indexes).item()
print(n_matches, n_matches / n_predicted, n_matches / n_actual)
处理时间序列
增加时间序列
import numpy as np
import torch
torch.set_printoptions(edgeitems=2, threshold=50, linewidth=75)
bikes_numpy = np.loadtxt(
"filepath/hour-fixed.csv",
dtype=np.float32,
delimiter=",",
skiprows=1,
converters={1: lambda x: float(x[8:10])}) # <1>
bikes = torch.from_numpy(bikes_numpy)
按时间段调整数据
利用view来调整查看数据的形式,使其以24小时为一个单位
同时将其变成
N
×
C
×
L
N\times C\times L
N×C×L(序列,通道,小时)的形式,还需要对其急性转置
daily_bikes = bikes.view(-1, 24, bikes.shape[1])
daily_bikes = daily_bikes.transpose(1, 2)
调整天气变量
天气状况有四个级别,将其调整为向量并与数据集连接起来
first_day = bikes[:24].long()
weather_onehot = torch.zeros(first_day.shape[0], 4)
weather_onehot.scatter_(
dim=1,
index=first_day[:,9].unsqueeze(1).long() - 1,
value=1.0)
torch.cat((bikes[:24], weather_onehot), 1)
daily_weather_onehot = torch.zeros(daily_bikes.shape[0], 4,
daily_bikes.shape[2])
daily_weather_onehot.scatter_(
1, daily_bikes[:,9,:].long().unsqueeze(1) - 1, 1.0)
daily_bikes = torch.cat((daily_bikes, daily_weather_onehot), dim=1)
但是这不是调整天气变量的唯一的方法,因为标签是有序数据关系,所以可以假设是连续变量的特殊值,可以用转换变量来处理,下面代码展示了几种方法:
daily_bikes[:, 9, :] = (daily_bikes[:, 9, :] - 1.0) / 3.0
temp = daily_bikes[:, 10, :]
temp_min = torch.min(temp)
temp_max = torch.max(temp)
daily_bikes[:, 10, :] = ((daily_bikes[:, 10, :] - temp_min)
/ (temp_max - temp_min))
temp = daily_bikes[:, 10, :]
daily_bikes[:, 10, :] = ((daily_bikes[:, 10, :] - torch.mean(temp))
/ torch.std(temp))
表示文本
不想写了直接贴代码做备份
import numpy as np
import torch
torch.set_printoptions(edgeitems=2, threshold=50)
with open('../data/p1ch4/jane-austen/1342-0.txt', encoding='utf8') as f:
text = f.read()
lines = text.split('\n')
line = lines[200]
line
letter_t = torch.zeros(len(line), 128) # <1>
letter_t.shape
for i, letter in enumerate(line.lower().strip()):
letter_index = ord(letter) if ord(letter) < 128 else 0 # <1>
letter_t[i][letter_index] = 1
def clean_words(input_str):
punctuation = '.,;:"!?”“_-'
word_list = input_str.lower().replace('\n',' ').split()
word_list = [word.strip(punctuation) for word in word_list]
return word_list
words_in_line = clean_words(line)
line, words_in_line
word_list = sorted(set(clean_words(text)))
word2index_dict = {word: i for (i, word) in enumerate(word_list)}
len(word2index_dict), word2index_dict['impossible']
word_t = torch.zeros(len(words_in_line), len(word2index_dict))
for i, word in enumerate(words_in_line):
word_index = word2index_dict[word]
word_t[i][word_index] = 1
print('{:2} {:4} {}'.format(i, word_index, word))
print(word_t.shape)
word_t = word_t.unsqueeze(1)
word_t.shape
[(c, ord(c)) for c in sorted(set(text))]
ord('l')