04 數(shù)據(jù)操作 + 數(shù)據(jù)預(yù)處理【動(dòng)手學(xué)深度學(xué)習(xí)v2】

數(shù)據(jù)操作和數(shù)據(jù)預(yù)處理
N維數(shù)組
- 機(jī)器學(xué)習(xí)中使用最多的是N維數(shù)組


- 最簡(jiǎn)單的N為數(shù)組是0維數(shù)組,稱之為標(biāo)量,可能表示一個(gè)物體的類別
- 一維數(shù)組稱為向量,比如說特征向量(將一個(gè)樣本抽象成一行數(shù)字)
- 二維數(shù)組稱為矩陣,比如說特征矩陣(行表示樣本,列表示特征)
- 三維數(shù)組最簡(jiǎn)單的就是一張RGB圖片(寬 (列數(shù))* 高(行數(shù)) * 通道數(shù)(R、G、B))
- n個(gè)三維數(shù)組放在一塊就變成了四維數(shù)組,比如說一個(gè)RGB圖片的批量(batch,深度學(xué)習(xí)中一個(gè)batch中通常會(huì)有128張圖片)
- 五維數(shù)組舉例:一個(gè)視頻的批量(在圖片批量的基礎(chǔ)上新增了時(shí)間的維度:批量大小 * 時(shí)間 * 寬 * 高 * 通道)
創(chuàng)建數(shù)組
創(chuàng)建數(shù)組需要:
- 形狀:矩陣的尺寸,比如3 * 4
- 每個(gè)元素的數(shù)據(jù)類型:比如32位浮點(diǎn)數(shù)
- 每個(gè)元素的值:例如全是0或者隨機(jī)數(shù)(正態(tài)分布、均勻分布)

訪問元素

- 元素行和列的下標(biāo)都是從0開始的
- 上圖中第三個(gè)例子有誤,一列應(yīng)該是[:,1]
- [1:3,1:]:“:”后面沒有數(shù)字的話表示取到行或者列的末尾
- [::3,::2]:從第零行到最后一行,每三行一跳;從第零列到最后一列,每?jī)闪幸惶?/li>
數(shù)據(jù)操作
1、導(dǎo)入torch(注意:雖然它被稱為PyTorch,但是實(shí)際上應(yīng)該導(dǎo)入Torch)
import torch
此處如果報(bào)錯(cuò)“no module named torch”,可以到pytorch的官網(wǎng)(直接百度pytorch就能找到pytorch的官網(wǎng))復(fù)制pytorch的安裝命令進(jìn)行安裝
2、張量表示一個(gè)數(shù)值組成的數(shù)組,這個(gè)數(shù)組可能有多個(gè)維度
x = torch.arange(12)
x
- arange(12):0 - 12 所有的數(shù),不包含12(左閉右開)
輸出:
tensor([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
3、可以通過shape屬性來訪問張量的形狀和張量中元素的總數(shù)
x.shape
- 注意shape是屬性,所以沒有括號(hào)
輸出:
torch.Size([12])
x.numel()
- number of elements
輸出:
12
4、要改變一個(gè)張量的形狀而不改變?cè)財(cái)?shù)量和元素值,可以調(diào)用reshape函數(shù)
x = x.reshape(3,4)
x
- 轉(zhuǎn)換成三行四列的矩陣
輸出:
tensor([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
5、使用全0、全1、其他常量或者從特定分布中隨機(jī)采樣的數(shù)字
torch.zeros((2,3,4))
輸出:
tensor([[[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]],
[[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]]])
torch.ones((2,3,4))
輸出:
tensor([[[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]],
[[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]]])
torch.tensor([[1,2,3,4],[3,4,1,2],[4,3,2,1]])
- 通過提供包含數(shù)值的python列表(或嵌套列表)來為所需張量中的每個(gè)元素賦予確定的值
輸出:
tensor([[1, 2, 3, 4],
[3, 4, 1, 2],
[4, 3, 2, 1]])
6、常見的標(biāo)準(zhǔn)算術(shù)運(yùn)算符
x = torch.tensor([1,2,3,4])
y = torch.tensor([4,3,2,1])
x + y , x - y , x * y , x / y , x ** y
- +
- -
- *
- /
- ** : 冪運(yùn)算
輸出:
(tensor([5, 5, 5, 5]),
tensor([-3, -1, 1, 3]),
tensor([4, 6, 6, 4]),
tensor([0.2500, 0.6667, 1.5000, 4.0000]),
tensor([1, 8, 9, 4]))
- 所有的運(yùn)算都是按元素進(jìn)行的
7、指數(shù)運(yùn)算
torch.exp(x)
輸出:
tensor([ 2.7183, 7.3891, 20.0855, 54.5981])
- 也是按照元素進(jìn)行的
8、將多個(gè)張量連結(jié)在一起
x = torch.arange(12,dtype = torch.float32).reshape((3,4))
y = torch.tensor([[1,2,3,4],[3,4,1,2],[4,3,2,1]])
torch.cat((x,y),dim=0),torch.cat((x,y),dim=1)
- dtype = torch.float32:聲明生成的張量元素的數(shù)據(jù)類型
- dim = 0:按第 0 維進(jìn)行合并,即按行進(jìn)行合并
- dim = 1:按第 1 維進(jìn)行合并,即按列進(jìn)行合并
- 這里的合并可以理解為在某個(gè)維度上進(jìn)行堆疊,該維度的元素?cái)?shù)量相加得到合并后的某一維度的元素?cái)?shù)量
輸出:
(tensor([[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.],
[ 1., 2., 3., 4.],
[ 3., 4., 1., 2.],
[ 4., 3., 2., 1.]]),
tensor([[ 0., 1., 2., 3., 1., 2., 3., 4.],
[ 4., 5., 6., 7., 3., 4., 1., 2.],
[ 8., 9., 10., 11., 4., 3., 2., 1.]]))
9、通過邏輯運(yùn)算符構(gòu)建二元張量
x == y
輸出:
tensor([[False, False, False, False],
[False, False, False, False],
[False, False, False, False]])
- 按元素值進(jìn)行判定
10、對(duì)張量中的所有元素進(jìn)行求和會(huì)產(chǎn)生一個(gè)只有一個(gè)元素的張量
x.sum()
輸出:
tensor(66.)
- 最終得到只有一個(gè)元素的標(biāo)量
11、即使形狀不同,依然可以通過調(diào)用廣播機(jī)制(bradcasting mechanism)來執(zhí)行按元素操作
a = torch.arange(3).reshape((3,1))
b = torch.arange(2).reshape((1,2))
a,b
輸出:
(tensor([[0],
[1],
[2]]),
tensor([[0, 1]]))
a + b
輸出:
tensor([[0, 1],
[1, 2],
[2, 3]])
- a , b會(huì)自動(dòng)補(bǔ)全稱為 3 * 2 的矩陣
12、可以使用[-1]選擇最后一個(gè)元素,可以使用[1:3]選擇第二和第三個(gè)元素
x,x[-1],x[1:3]
輸出:
(tensor([[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.]]),
tensor([ 8., 9., 10., 11.]),
tensor([[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.]]))
- x[-1]:表示取出最后一行所有的元素
- 注意這里的x[1:3]取的是第二行和第三行的數(shù)據(jù)(這里也是左閉右開,只取了1、2,3沒有取到)
13、除了讀取之外,還可以通過指定索引來將元素寫入矩陣
x[1,2] = 9
x
輸出:
tensor([[ 0., 1., 2., 3.],
[ 4., 5., 9., 7.],
[ 8., 9., 10., 11.]])
14、可以為多個(gè)元素賦相同的值,只需要索引所有的元素,然后為他們賦值
x[0:2,:] = 44
x
輸出:
tensor([[44., 44., 44., 44.],
[44., 44., 44., 44.],
[ 8., 9., 10., 11.]])
15、運(yùn)行一些操作可能會(huì)導(dǎo)致為新結(jié)果分配內(nèi)存
before = id(y)
y = y + x
id(y) == before
- id():返回變量在python中的唯一標(biāo)識(shí)號(hào)(十進(jìn)制)
輸出:
False
- y不等于before,說明y之前的內(nèi)存已經(jīng)被析構(gòu)掉了,新的y的id并不等于之前的y的id
執(zhí)行原地操作
z = torch.zeros_like(y)
print('id(z):',id(z))
z[:] = x + y
print('id(z):',id(z))
輸出:
id(z): 2370001081104
id(z): 2370001081104
- z的id和之前的id并沒有發(fā)生變化,并沒有為z創(chuàng)建新的內(nèi)存,這里可以理解為對(duì)z中的元素進(jìn)行改寫,而不是運(yùn)算,也就是說,預(yù)算會(huì)改變變量的內(nèi)存地址,但改寫變量中的元素并不會(huì)改變?cè)氐膬?nèi)存地址
16、如果在后續(xù)計(jì)算中并沒有重復(fù)使用x,也可以使用x[:] = x + y 或 x += y 來減少操作的內(nèi)存開銷
before = id(x)
x += y
id(x) == before
輸出:
True
- 使用“+=”運(yùn)算符并沒有改變變量的內(nèi)存地址
- 需要注意的是,在占用內(nèi)存比較大的情況下不要進(jìn)行過度復(fù)制
- 布爾值的首字母大寫
17、轉(zhuǎn)換為NumPy張量
- numpy是python中最基礎(chǔ)的多元數(shù)組運(yùn)算框架
a = x.numpy()
b = torch.tensor(a)
type(a),type(b)
- x.numpy():得到一個(gè)numpy的多元數(shù)組
輸出:
(numpy.ndarray, torch.Tensor)
- a的type是numpy.ndarray,b的type是torch.Tensor
18、將大小為1的張量轉(zhuǎn)換為Python標(biāo)量
a = torch.tensor([3.5])
a,a.item(),float(a),int(a)
- a.item():返回一個(gè)numpy的浮點(diǎn)數(shù)
輸出:
(tensor([3.5000]), 3.5, 3.5, 3)
數(shù)據(jù)預(yù)處理
- 對(duì)于原始數(shù)據(jù),如何進(jìn)行讀取,使得能夠使用機(jī)器學(xué)習(xí)的方法進(jìn)行處理
1、創(chuàng)建一個(gè)人工數(shù)據(jù)集,并存儲(chǔ)在csv(逗號(hào)分隔值)文件
import os
os.makedirs(os.path.join('..','data'),exist_ok=True)
data_file=os.path.join('..','data','house_tiny.csv')
with open(data_file,'w') as f:
? ? f.write('NumRooms,Alley,Price\n') # 列名
? ? f.write('NA,Pave,127500\n') # 每行表示一個(gè)數(shù)據(jù)樣本
? ? f.write('2,NA,10600\n')
? ? f.write('4,NA,178100\n')
? ? f.write('NA,NA,140000\n')
- 創(chuàng)建一個(gè)文件,文件名叫做house_tiny.csv
- csv:每一行是一個(gè)數(shù)據(jù),每個(gè)域(entry)用逗號(hào)分開
- NA:not a number,未知數(shù)
2、從創(chuàng)建的csv問及那中加載原始數(shù)據(jù)集
- 一般使用pandas庫(kù)進(jìn)行csv文件的讀取
import pandas as pd
data = pd.read_csv(data_file)
print(data)
輸出:
NumRooms Alley Price
0 NaN Pave 127500.0
1 2,NA 10600 NaN
2 4 NaN 178100.0
3 NaN NaN 140000.0
注意:
- 如果報(bào)錯(cuò)”no module named pandas“,在終端使用”pip install pandas“命令即可安裝pandas
- 如果程序報(bào)錯(cuò)”UnicodeDecodeError: 'utf-8' codec can't decode byte 0xa3 in position 39: invalid start byte“,需要將1中的with open(data_file,'w') as f:這行代碼改成with open(data_file,'w',encoding='utf-8') as f:即可
- 如果不使用print函數(shù)輸出data,則會(huì)以html的格式輸出data,如下圖所示:

3、為了處理缺失的數(shù)據(jù),典型的方法包括插值和刪除,此處考慮使用插值的方法
inputs,outputs = data.iloc[:,0:2],data.iloc[:,2]
inputs = inputs.fillna(inputs.mean())
print(inputs)
輸出:
NumRooms Alley
0 3.0 Pave
1 2.0 NaN
2 4.0 NaN
3 3.0 NaN
- 將NA使用該列其它非NA值的平均值進(jìn)行填充
4、對(duì)于inputs中的類別值或者離散值,將”NaN“視為一個(gè)類別
inputs = pd.get_dummies(inputs,dummy_na=True)
print(inputs)
- dummy_na=True:將Na也變成一個(gè)類,并賦值為1
輸出:
NumRooms Alley_Pave Alley_nan
0 3.0 1 0
1 2.0 0 1
2 4.0 0 1
3 3.0 0 1
對(duì)于不是數(shù)值的數(shù)據(jù)的處理方法
將缺失值做成一個(gè)特別的類,然后將其變成一個(gè)數(shù)值(而不是字符串,字符串相對(duì)來講不好處理),對(duì)數(shù)值的處理:將所有出現(xiàn)的不同種的值都變成一個(gè)特征
5、現(xiàn)在已經(jīng)把所有缺失的值和字符串變成了數(shù)值,inputs和outputs中的所有的條目都是數(shù)值類型,他們可以轉(zhuǎn)換為張量格式
import torch
x , y = torch.tensor(inputs.values),torch.tensor(outputs.values)
x , y
輸出:
(tensor([[3., 1., 0.],
[2., 0., 1.],
[4., 0., 1.],
[3., 0., 1.]], dtype=torch.float64),
tensor([127500, 10600, 178100, 140000]))
- dtype=torch.float64:傳統(tǒng)的python會(huì)默認(rèn)使用64位的浮點(diǎn)數(shù),但是64位的浮點(diǎn)數(shù)一般計(jì)算比較慢,對(duì)于深度學(xué)習(xí)來講,通常使用32位浮點(diǎn)數(shù)
Q&A
- 1、reshape和view的區(qū)別??數(shù)據(jù)操作 QA P4 - 00:17?
a = torch.arange(12)
b = a.reshape((3,4))
b[:] = 2
a
輸出:
tensor([2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2])
- 上述操作中,創(chuàng)建了一個(gè)張量并賦值給a,再將a的reshape賦值給b,修改b的元素后發(fā)現(xiàn)a中的元素也被修改了
- 其實(shí)a并沒有賦值給b,只是在b中創(chuàng)建了一個(gè)a的biew(數(shù)據(jù)庫(kù)中的視圖),形狀變了但是元素沒變,在改變b的時(shí)候,其實(shí)就是連帶著也將a進(jìn)行了修改
- 2、數(shù)組計(jì)算跟不上,需要補(bǔ)充python還是線性代數(shù)??數(shù)據(jù)操作 QA P4 - 02:22?
學(xué)習(xí)numpy的一些教材
- 3、如何快速區(qū)分維度??數(shù)據(jù)操作 QA P4 - 02:59?
可以用.shape進(jìn)行查看
- 4、torch的tensor和numpy的array類似嗎??數(shù)據(jù)操作 QA P4 - 03:18?
不類似。
- 5、tensor和array有什么區(qū)別??數(shù)據(jù)操作 QA P4 - 03:48?
tensor是一個(gè)數(shù)學(xué)上的概念,是一個(gè)張量,數(shù)學(xué)上面的定義
array是計(jì)算機(jī)中的語言,數(shù)組,多元數(shù)組是一個(gè)計(jì)算機(jī)中的概念,沒有數(shù)學(xué)上的定義
pytorch的tensor重載了數(shù)學(xué)上的張量的概念
tensor和數(shù)組本質(zhì)上沒有任何區(qū)別,不必糾結(jié)數(shù)學(xué)上的定義,其實(shí)就是一個(gè)東西
- 6、有圖形化的torch編程方案嗎??數(shù)據(jù)操作 QA P4 - 04:48?
沒有太多好的可視化工具,可以多多運(yùn)行代碼來理解高維數(shù)組這個(gè)概念上的東西
- 7、新分配的y的內(nèi)存,之前y的內(nèi)存會(huì)釋放嗎??數(shù)據(jù)操作 QA P4 - 05:40?
如果這個(gè)內(nèi)存沒有別的地方使用的話,python會(huì)自動(dòng)釋放,不必過于擔(dān)心內(nèi)存的事情
- 8、pytorch、mxnet實(shí)現(xiàn)梯度反傳是?
這個(gè)問題以后會(huì)講
- 9、能使用**來替代numpy嗎?
- 10、上個(gè)版本用leview來改變形狀且共用內(nèi)存,第二個(gè)版本都在用reshape??數(shù)據(jù)操作 QA P4 - 06:54?
reshape和view其實(shí)是一回事,reshape也是改變形狀創(chuàng)建一個(gè)view
補(bǔ)充:

----to be continued----
其它參考:
1、《動(dòng)手學(xué)深度學(xué)習(xí)》,課程安排,https://courses.d2l.ai/zh-v2/assets/pdfs/part-0_4.pdf
2、《動(dòng)手學(xué)深度學(xué)習(xí)》,https://zh-v2.d2l.ai/chapter_preliminaries/ndarray.html