# Pytorch ML 코드 중심으로 알아보기
### tensor dimension

### tensor initialization
```python
ndarray = [[1,2],[3,4]]
tensor_1 = torch.tensor(ndarray) # ndarray -> tensor
nparray = np.array(ndarray) # ndarray -> numpy
tensor_2 = torch.from_numpy(nparray) # numpy -> tensor
tensor_3 = torch.ones_like(tensor_1) # tensor -> tensor (차원: 동일, 값: 1)
tensor_3 = torch.rand_like(tensor_1) # tensor -> tensor (차원: 동일, 값: random)
shape = (2,2) # 차원 모양
torch.rand(shape) # shape 모양의 random 값 tensor
torch.ones(shape) # shape 모양의 모든값이 1인 tensor
torch.zeros(shape) # shape 모양의 모든값이 random인 tensor
```
### tensor 특성
- bridge: numpy와 tensor는 동일한 memory 공간을 공유
```python
tensor = torch.tensor([1,1])
tensor_np = tensor.numpy()
tensor.add_(1) # = tensor([2,2])
print(tensor_np) # = [2,2]
```
- broadcasting: dimension이 다른 두 tensor간 연산이 가능하도록 조정
```python
m1 = torch.FloatTensor([[1, 2]])
m2 = torch.FloatTensor([3]) # m2: [3] -> [3,3]
print(m1 + m2) # = tensor([[4., 5.]])
m1 = torch.FloatTensor([[1, 2]]) # m1: [[1,2]] -> [[1,2],[1,2]]
m2 = torch.FloatTensor([[3], [4]]) # m2: [3],[4] -> [[3, 3],[4, 4]]
print(m1 + m2) # = tensor([4., 5.],[5., 6.]])
```
### tensor 주요 함수
- tensor를 GPU로 옴기기
```python
if torch.cuda.is_available():
tensor = tensor.to('cuda')
```
- 행/열 값 변경
```python
tensor[:,0] = 0 # 1번째 column값 전부 0
tensor[0,:] = 0 # 1번째 row값 전부 0
tensor.add_(1) # 모든 element에 1을 더함
```
- 합치기
```python
tensor_a, tensor_b # shape (3,2) tensors
torch.cat([tensor_a, tensor_b)], dim=0) # result shape (6,4)
torch.stack([tensor_a, tensor_b)], dim=0) # result shape (2,3,4)
```
- 덧샘
```python
tensor = torch.FloatTensor([[1, 2], [3, 4]])
tensor.sum() # = tensor(10.)
tensor.sum(dim=0) # = tensor([4., 6.]) 밖에서 부터 첫번째 차원
tensor.sum(dim=1) # = tensor([3., 7.]) 밖에서 부터 두번째 차원
tensor.sum(dim=-1) # = tensor([3., 7.]) 안쪽에서 부터 첫번째 차원
tensor.sum(dim=-2) # = tensor([4., 6.]) 안쪽에서 부터 두번째 차원
```
- 곱샘
```python
tensor = torch.tensor([1,1])
tensor.mul(tensor) # = tensor([1, 1]) element-wise product
tensor * tensor # = tensor([1, 1]) element-wise product
tensor.matmul(tensor.T) # = tensor(2) matrix multiplcation
tensor @ tensor.T # = tensor(2) matrix multiplcation
```
- 평균
```python
tensor = torch.FloatTensor([[1, 2], [3, 4]])
tensor.mean() # = tensor(2.5000)
t.mean(dim=0) # = tensor([2., 3.])
t.mean(dim=1) # = tensor([1.5000, 3.5000])
```
- 최대값
```python
tensor = torch.FloatTensor([[1, 2], [3, 4]])
tensor.max() # = tensor(4.)
tensor.max(dim=0) # = (tensor([3., 4.]), tensor([1, 1]))
tensor.max(dim=0)[0] # = tensor([3., 4.]) dim=0 기준 max 값
tensor.max(dim=0)[1] # = tensor([1, 1]) dim=0 기준 argmax 값 (각 index)
```
- dimension 변경
```python
tensor_a.shape # = torch.Size([1, 2, 3])
tensor_a.view(-1,3).shape # = torch.Size([2,3]) 1 * 2 * 3 = ? * 3
tensor_a.view(-1,2,3).shape # = torch.Size([1,2,3]) 1 * 2 * 3 = ? * 2 * 3
tensor_b.shape # = torch.Size([1,2,1,2])
tensor_b.squeeze().shape # = torch.Size([2,2]) 값이 1인 차원을 제거
tensor_c.shape # = torch.Size([2,2])
tensor_c.unsqueeze(0).shape # = torch.Size([1,2,2]) 0번째 위치에 차원 추가
tensor_c.unsqueeze(1).shape # = torch.Size([2,1,2]) 1번째 위치에 차원 추가
```
- type casting
```python
lt = torch.LongTensor([1, 2, 3, 4])
lt.float() # = tensor([1., 2., 3., 4.])
bt = torch.BoolTensor([True, False, False, True])
bt.byte() # = tensor([1, 0, 0, 1], dtype=torch.uint8)
```
- 덮어쓰기
```python
x = torch.FloatTensor([[1, 2], [3, 4]])
x.mul(2.) # = tensor([[2, 4], [6, 8]])
x # = tensor([[1, 2], [3, 4]]) x 변수는 변하지 않음
x.mul_(2.) # = tensor([[2, 4], [6, 8]]) 함수 뒤에 _를 사용하면 변수 덮어쓰기
x # = tensor([[2, 4], [6, 8]])
```
- 난수 발생 seed 설정
```python
torch.manual_seed(3) # 3번째 random seed
for i in range(1,3):
print(torch.rand(1)) # tensor([0.0043]) , tensor([0.1056])
```
### linear regression
```python
x_train = torch.FloatTensor([[1], [2], [3]]) # 공부에 투자한 시간 1,2,3 시간
y_train = torch.FloatTensor([[2], [4], [6]]) # 시험 성적 2,4,6 점
# W의 차원은 x의 개수에 비례한다. x가 3개면 W는 (3,1)이 된다.
# 지금 x의 개수는 시간이라는 변수 1개이며 총 3개의 데이터가 있으니 W의 차원은 1이 된다.
W = torch.zeros(1, requires_grad=True) # requires_grad는 학습되는 값을 추적하기 위함
b = torch.zeros(1, requires_grad=True)
optimizer = optim.SGD([W, b], lr=0.01)
nb_epochs = 1999 # 원하는만큼 경사 하강법을 반복
for epoch in range(nb_epochs + 1):
hypothesis = x_train.matmul(W) + b # H(x) = Wx + b x_train.matmul(W) + b
cost = torch.mean((hypothesis - y_train) ** 2) # cost = avg((H(x) - y)^2)
optimizer.zero_grad() # optimizer의 기울기는 누적되는 특징 때문에 0으로 초기화
cost.backward() # 비용함수 미분하여 gradient 계산
optimizer.step() # W와 b 업데이트
if epoch % 100 == 0: # 100번마다 로그 출력
print('Epoch {:4d}/{} W: {:.3f}, b: {:.3f} Cost: {:.6f}'.format(
epoch, nb_epochs, W.item(), b.item(), cost.item()
))
```