Skip to content
272 changes: 272 additions & 0 deletions site/ko/guide/tensor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
# 텐서플로 텐서

이름에서 알 수 있듯이, 텐서플로는 텐서를 포함한 계산을 정의하고 실행하는 프레임워크입니다.
*텐서(tensor)*는 벡터와 행렬을 일반화한 것이고 고차원으로 확장 가능합니다.
내부적으로 텐서플로는 기본적으로 제공되는 자료형을 사용해 n-차원 배열로 나타냅니다.

텐서플로 프로그램을 작성할 때, `tf.Tensor`를 주로 조작하고 전달합니다.
`tf.Tensor` 객체는 부분적으로 정의된 연산으로 표현되고 이것은 결국 값으로 변환됩니다.
텐서플로 프로그램은 `tf.Tensor` 객체 그래프를 만드는 것으로 먼저 시작하고,
각각의 텐서가 다른 텐서를 기반으로 어떤 식으로 계산될 수 있는지 구체화하고,
그 다음 그래프를 실행해서 원하는 결과를 얻게 됩니다.

`tf.Tensor`는 다음과 같은 속성을 가지고 있습니다:

* 자료형 (예를 들어, `float32` 또는 `int32`, `string`)
* 형태(shape)


텐서안의 각각 원소는 동일한 자료형이고 항상 그 자료형을 알 수 있습니다.
형태(즉, 차원 수와 각 차원마다 길이)는 일부만 알 수 있습니다.
대부분 연산은 입력값 형태를 알 수 있다면 형태가 완전하게 정의된 텐서를 만들지만,
일부 경우에서는 그래프를 실행한 이후에 텐서 형태를 알 수 있기도 합니다.

일부 특별한 텐서는 텐서플로 가이드문서의 다른 부분에서 다뤄질 것입니다.
핵심인 텐서는 다음과 같습니다:

* `tf.Variable`
* `tf.constant`
* `tf.placeholder`
* `tf.SparseTensor`

예외인 `tf.Variable`을 제외한다면, 텐서 값은 변경할 수 없습니다.
즉, 텐서를 한번 실행시킨 경우에는 오직 하나의 값만을 가집니다.
그러나, 동일한 텐서를 다시 실행시킨다면 다른 값을 가질 수 있습니다;
예를 들어 텐서가 디스크로부터 데이터를 읽어들인 결과이거나,
무작위 숫자를 생성하는 경우입니다.

## 랭크(Rank)

`tf.Tensor` 객체의 **랭크**는 그 차원의 수입니다.
랭크의 동의어는 **order** 또는 **degree**, **n-dimension**입니다.
텐서플로의 랭크는 수학에서 사용하는 행렬의 랭크와는 다릅니다.
다음 표에서 알 수 있는 것처럼, 텐서플로의 각 랭크는 각각 다른 수학 개체(entity)에 해당됩니다.

랭크 | 수학 개체
--- | ---
0 | 스칼라(Scalar) (크기(magnitude)만)
1 | 벡터(Vector) (크기와 방향(direction))
2 | 행렬(Matrix) (숫자 표)
3 | 3-텐서 (숫자 큐브(cube))
n | n-텐서 (알 수 있을겁니다(you get the idea))


### 랭크 0

다음은 랭크 0 변수 생성 예입니다:

```python
mammal = tf.Variable("코끼리", tf.string)
ignition = tf.Variable(451, tf.int16)
floating = tf.Variable(3.14159265359, tf.float64)
its_complicated = tf.Variable(12.3 - 4.85j, tf.complex64)
```

Note: 문자열은 텐서에서 문자 시퀀스(sequence)가 아니라 단일 객체로 다뤄집니다.
객체는 단일 문자열과 문자열 벡터 등 모두 가능합니다.

### 랭크 1

랭크 1 `tf.Tensor` 객체를 생성하기 위해서 초기값으로 리스트를 사용할 수 있습니다.
예를 들어:

```python
mystr = tf.Variable(["안녕하세요"], tf.string)
cool_numbers = tf.Variable([3.14159, 2.71828], tf.float32)
first_primes = tf.Variable([2, 3, 5, 7, 11], tf.int32)
its_very_complicated = tf.Variable([12.3 - 4.85j, 7.5 - 6.23j], tf.complex64)
```


### 고차원 랭크

랭크 2 `tf.Tensor` 객체는 최소 한 개 이상의 열과 행으로 구성됩니다:

```python
mymat = tf.Variable([[7],[11]], tf.int16)
myxor = tf.Variable([[False, True],[True, False]], tf.bool)
linear_squares = tf.Variable([[4], [9], [16], [25]], tf.int32)
squarish_squares = tf.Variable([ [4, 9], [16, 25] ], tf.int32)
rank_of_squares = tf.rank(squarish_squares)
mymatC = tf.Variable([[7],[11]], tf.int32)
```

유사하게, 고차원 랭크 텐서는 n-차원 배열로 구성됩니다.
예를 들어, 이미지 처리에서는 각각 배치 수와 이미지 높이, 이미지 너비, 색상 채널에 해당하는 4차원 랭크 텐서가 사용됩니다.

``` python
my_image = tf.zeros([10, 299, 299, 3]) # 배치 x 높이 x 너비 x 색상
```

### `tf.Tensor` 객체 랭크 구하기

`tf.Tensor` 객체의 랭크를 알기 위해서는 `tf.rank` 메서드를 호출합니다.
예를 들어, 다음 메서드는 이전 섹션에서 정의된 `tf.Tensor`의 랭크를 알려줍니다.

```python
r = tf.rank(my_image)
# 이 코드가 실행된 후 r은 4라는 값을 가지게 됩니다.
```

### `tf.Tensor` 원소 참조하기

`tf.Tensor`는 n-차원 배열로 구성되어 있기 때문에 때문에,
`tf.Tensor`의 원소 하나에 접근하기 위해서는 n개의 인덱스가 필요합니다.

랭크 0 텐서(스칼라)인 경우 이미 하나의 숫자이기 때문에 인덱스가 필요없습니다.

랭크 1 텐서(벡터)인 경우 숫자 하나에 접근하기 위해서는 인덱스 한 개를 전달해야 합니다:

```python
my_scalar = my_vector[2]
```

벡터로부터 원소 한 개를 동적으로 선택하기 위해서
`[]`안에 스칼라형 `tf.Tensor`를 인덱스로 사용할 수 있습니다.

랭크 2이상의 고차원 텐서인 경우에는 좀 더 흥미롭습니다.
예상한 것처럼 랭크 2인 `tf.Tensor`를 위해 인덱스로 2개를 전달해야 스칼라 한 개를 반환합니다:


```python
my_scalar = my_matrix[1, 2]
```


그러나, 한 개만 전달한다면 다음과 같이 행렬의 부분 벡터를 반환합니다:


```python
my_row_vector = my_matrix[2]
my_column_vector = my_matrix[:, 3]
```

`:` 표기는 "해당 차원을 남겨라"라는 파이썬 슬라이싱(slicing) 문법입니다.
이러한 표기법은 고차원 텐서에서 부분 벡터와 부분 행렬, 다른 부분 텐서들까지도 접근할 수 있도록 만들어 주기 때문에 유용합니다.


## 형태

텐서의 **형태(shape)**는 각 차원에 있는 원소 개수로 표현됩니다.
텐서플로는 그래프 계산 과정에서 자동으로 텐서 형태를 추론합니다.
이렇게 추론된 형태는 랭크를 알고 있는 경우도 있고 그렇지 않는 경우도 있습니다.
만약에 랭크를 알고 있는 경우라도 각 차원의 원소 개수를 알고 있는 경우도 있고 그렇지 않는 경우도 있습니다.

텐서플로 문서에서 텐서 차원을 표현하기 위해서 3가지 용어를 사용합니다: 랭크, 형태, 차원.
다음 표는 각 용어가 다른 용어와 어떻게 연관되어 있는지를 보여줍니다.

랭크 | 형태 | 차원 | 예제
--- | --- | --- | ---
0 | [] | 0-차원 | 스칼라인 0-차원 텐서.
1 | [D0] | 1-차원 | 형태가 [5]인 1-차원 텐서.
2 | [D0, D1] | 2-차원 | 형태가 [3, 4]인 2-차원 텐서.
3 | [D0, D1, D2] | 3-차원 | 형태가 [1, 4, 3]인 3-차원 텐서.
n | [D0, D1, ... Dn-1] | n-차원 | 형태가 [D0, D1, ... Dn-1]인 텐서.

형태는 파이썬 리스트 / 정수형 튜플 또는 `tf.TensorShape`으로 표현될 수 있습니다.

### `tf.Tensor` 객체 형태 얻기

`tf.Tensor`의 형태를 알기 위한 2가지 방법이 있습니다.
그래프를 생성하는 동안 텐서의 형태를 알 수 있는 것은 종종 유용합니다.
형태는 `tf.Tensor`객체의 `shape` 속성으로 알 수 있습니다.
이 메서드는 `TensorShape`를 반환하고, 이러한 방식은 완전하게 알지 못하는 형태를 표현하는데 편리한 방법입니다
(그래프를 생성할 때 모든 형태를 알 수 없기 때문에).

실행 시점에 다른 `tf.Tensor` 객체의 완전한 형태를 표현하는 `tf.Tensor`를 얻을 수도 있습니다.
이것은 `tf.shape` 연산을 통해서 확인할 수 있습니다.
이를 통해, 입력 `tf.Tensor`의 동적인 형태에 연동된 다른 텐서를 생성함으로써
텐서 형태를 변경하는 그래프를 생성할 수 있습니다.

예를 들어 다음은 주어진 행렬의 열 개수와 동일한 크기의 0으로 구성된 벡터를 만드는 예입니다.

``` python
zeros = tf.zeros(my_matrix.shape[1])
```

### `tf.Tensor` 형태 변경

텐서의 **원소 개수**는 모든 형태의 크기를 곱한 것입니다.
스칼라의 원소 개수는 항상 `1`입니다.
원소 개수가 같은 경우에도 형태는 다양할 수 있기 때문에,
그 원소 개수를 유지하면서 `tf.Tensor`의 형태를 변경할 수 있는 것은 유용합니다.
이것은 `tf.reshape`으로 처리할 수 있습니다.

다음은 텐서 형태를 변경하는 예입니다:

```python
rank_three_tensor = tf.ones([3, 4, 5])
matrix = tf.reshape(rank_three_tensor, [6, 10]) # 기존 내용을 6x10 행렬로
# 형태 변경
matrixB = tf.reshape(matrix, [3, -1]) # 기존 내용을 3x20 행렬로 형태 변경
# -1은 차원 크기를 계산하여
# 자동으로 결정하라는 의미
matrixAlt = tf.reshape(matrixB, [4, 3, -1]) # 기존 내용을 4x3x5 텐서로
# 형태 변경

# 형태가 변경된 텐서의 원소 개수는
# 원래 텐서의 원소 개수와 같습니다.
# 그러므로 다음은 원소 개수를 유지하면서
# 마지막 차원에 사용 가능한 수가 없기 때문에 에러를 발생합니다.
yet_another = tf.reshape(matrixAlt, [13, 2, -1]) # 에러!
```

## 자료형

차원뿐만 아니라, 텐서는 자료형도 가지고 있습니다.
전체 자료형을 확인하려면 `tf.DType`를 참고하세요.

`tf.Tensor`가 한 개이상의 자료형을 가지는 것은 불가능합니다.
임의의 데이터 구조를 직렬화한 `string`를 저장한 `tf.Tensor`는 예외입니다.

`tf.cast`를 이용해서 `tf.Tensor`의 자료형을 다른 것으로 변경하는 것은 가능합니다:

``` python
# 정수형 텐서를 실수형으로 변환.
float_tensor = tf.cast(tf.constant([1, 2, 3]), dtype=tf.float32)
```

`tf.Tensor`의 자료형을 확인하기 위해서는 `Tensor.dtype` 속성을 활용하세요.

파이썬 객체를 이용해서 `tf.Tensor`를 생성할 때 자료형을 선택적으로 명시할 수 있습니다.
그렇지 않으면 텐서플로가 데이터 표현에 적합한 자료형을 선택합니다.
텐서플로는 파이썬 정수를 `tf.int32`로 변환하고 파이썬 실수는 `tf.float32`으로 변환합니다.
그외에 동일한 규칙을 넘파이(numpy)를 배열로 변경할 때 사용합니다.

## 텐서 계산하기(evaluate)

일단 계산 그래프를 만들었다면, 특정 `tf.Tensor`를 생성하거나 텐서에 할당된 값을 가져오는 계산을 실행할 수 있습니다.
이것은 텐서플로 작업의 많은 부분에서 필요할 뿐 아니라 디버깅에 종종 유용합니다.

텐서를 계산하는 가장 간단한 방법은 `Tensor.eval` 메서드를 사용하는 것입니다. 예를 들어:

```python
constant = tf.constant([1, 2, 3])
tensor = constant * constant
print(tensor.eval())
```

`eval` 메서드는 기본 `tf.Session`이 활성화된 경우에만 작동합니다
(자세한 내용은 [그래프와 세션](./graphs.md)에서 확인하세요).

`Tensor.eval`은 텐서와 같은 내용을 가지는 넘파이 배열을 반환합니다.

때때로 현재 사용할 수 없는 동적인 정보에 의존하기 때문에
컨텍스트(context)가 없는 `tf.Tensor`는 계산할 수 없는 경우가 있습니다.
예를 들어, `placeholder`에 의존하는 텐서는 그 `placeholder`에 해당하는 값이 제공되지 않으면 계산할 수 없습니다.

``` python
p = tf.placeholder(tf.float32)
t = p + 1.0
t.eval() # 플레이스홀더(placeholder)가 값을 가지고 있지 않기 때문에, 이것은 실패할 것입니다.
t.eval(feed_dict={p:2.0}) # 플레이스홀더에 해당하는 값을 제공받기 때문에
# 이것은 성공할 것입니다.
```

플레이스홀더뿐만 아니라 어떤 `tf.Tensor`도 값을 제공받는 것이 가능하다는 것에 유의하세요.

다른 모델 구조는 `tf.Tensor`를 계산하는 것을 복잡하게 만들 수 있습니다.
텐서플로는 함수안이나 제어 흐름안에 정의된 `tf.Tensor`를 직접 계산할 수 없습니다.
만약에 `tf.Tensor`가 큐(queue)에 있는 값을 사용한다면,
무언가가 큐에 들어간 후에 만 `tf.Tensor` 계산을 할 수 있습니다; 그렇지 않으면 계산은 멈출(hang) 것입니다.
큐와 같이 작업할 때, `tf.Tensor`를 계산하기 전 `tf.train.start_queue_runners`를 호출하세요.