벡터와 행렬
신경망이 보는 세상에는 사진도, 문장도, 사용자도 없다. 오직 숫자 배열과 그 배열을 다른 배열로 바꾸는 규칙만 있다. 앞의 것이 벡터이고 뒤의 것이 행렬이다. GPT가 다음 단어를 고르는 과정도 결국 수천 차원 벡터들의 내적과 거대한 행렬곱의 연속이다. 이 장에서는 이후 모든 장 — 임베딩, 어텐션, Transformer, 양자화, 서빙 — 에서 쓰일 선형대수의 직관을 손으로 만지며 쌓는다.
- 이미지·단어·사용자 취향이 어떻게 하나의 특징 공간 속 점(벡터)이 되는지 설명할 수 있다.
- 내적·코사인 유사도·유클리드 거리의 관계를 알고, 정규화 여부에 따라 검색 순위가 어떻게 달라지는지 예측할 수 있다.
- 2×2 행렬을 기저 벡터의 도착지로 읽고, 행렬식(넓이 배율·뒤집힘)과 고유벡터의 기하학적 의미를 안다.
- 행렬곱을 '행·열 내적의 표'와 '변환의 합성' 두 관점으로 이해하고, FLOPs = 2mnk로 연산량을 셀 수 있다.
- 신경망 층 \(\mathbf y = W\mathbf x + \mathbf b\)를 배치·시퀀스 차원까지 확장한 텐서 shape로 읽고, GPU가 왜 행렬곱 기계인지 설명할 수 있다.
- 고차원에서 랜덤 벡터가 거의 직교하고 거리가 집중되는 현상, 노름·L2 정규화·투영·PCA의 의미를 이해한다.
왜 벡터인가: 모든 것은 숫자 배열이다
컴퓨터가 고양이 사진을 '본다'는 것은 픽셀 밝기 숫자들을 읽는다는 뜻이다. 28×28 흑백 손글씨 숫자 이미지는 784개의 숫자이고, 224×224 컬러 사진은 224×224×3 = 150,528개의 숫자다. 1초짜리 16 kHz 음성은 16,000개의 숫자다. 이 숫자들을 한 줄로 늘어놓으면 벡터(vector)가 된다. 784개 숫자로 된 이미지는 784차원 공간의 점 하나다.
단어는 조금 다르다. '고양이'라는 글자 자체에는 숫자가 없다. 그래서 언어 모델은 어휘의 각 토큰에 학습 가능한 벡터를 하나씩 배정한다. GPT-2 small은 토큰마다 768개, Llama 3 8B는 4096개의 숫자를 쓴다. 이것을 임베딩(embedding)이라 하며 4장의 주제다. 추천 시스템에서는 사용자의 취향을 '액션 4점, 로맨스 0점, SF 3점…' 같은 벡터로 쓰고, 영화도 같은 축 위의 벡터로 쓴다.
벡터의 각 성분이 사는 축들이 이루는 공간을 특징 공간(feature space)이라 한다. 특징 공간의 진짜 힘은 기하학이 의미를 갖게 된다는 데 있다. 비슷한 사진은 가까운 점, 비슷한 단어는 비슷한 방향, 취향이 맞는 영화는 사용자 벡터와 각도가 작은 벡터다. 학습이란 결국 이 공간을 잘 만들어서 '의미상 가까운 것'이 '기하학적으로 가까운 것'이 되도록 숫자를 조정하는 과정이라고 봐도 좋다.
이 책에서 벡터는 굵은 소문자 \(\mathbf x\), 행렬은 대문자 \(W\), 스칼라는 보통 글씨 \(a\)로 쓴다. 벡터는 기본적으로 열벡터(세로)이며 \(\mathbf x \in \mathbb R^d\)는 "실수 \(d\)개로 된 벡터"라는 뜻이다. 코드(NumPy·PyTorch)에서는 벡터를 1차원 배열 shape (d,)로, 여러 벡터를 행으로 쌓은 2차원 배열 shape (n, d)로 다룬다.
벡터의 기본 연산: 덧셈, 스칼라배, 크기, 방향
벡터 연산은 성분별로 한다. 두 벡터의 덧셈은 같은 위치 성분끼리 더하고, 스칼라배는 모든 성분에 같은 수를 곱한다. 기하학적으로 덧셈은 화살표를 이어 붙이는 것(또는 평행사변형의 대각선), 스칼라배는 화살표를 늘이거나 줄이는 것이다. 음수를 곱하면 방향이 반대가 된다.
뺄셈 \(\mathbf a - \mathbf b\)는 \(\mathbf b\)의 끝에서 \(\mathbf a\)의 끝으로 가는 화살표다. 그래서 두 점 사이의 거리는 \(\lVert\mathbf a - \mathbf b\rVert\)로 잰다. 언어 모델 임베딩에서 유명한 "king − man + woman ≈ queen" 같은 유추는, 두 벡터의 차이가 '성별'이라는 방향을 담고 있다는 가설을 뺄셈과 덧셈으로 확인하는 것이다(4장).
아래 놀이터에서 두 벡터를 직접 움직여 보자. 덧셈·뺄셈뿐 아니라 다음 절의 주인공인 내적과 투영도 함께 계산해 둔다.
2D 벡터 놀이터: 덧셈·뺄셈·투영·내적
내적 = 유사도 = 투영
이 책 전체에서 가장 많이 등장하는 연산 하나를 꼽으라면 내적(dot product, inner product)이다. 정의는 단순하다. 같은 위치 성분끼리 곱해서 모두 더한다. 그런데 이 단순한 계산이 기하학적으로 두 가지 의미를 동시에 갖는다.
첫째, 내적은 투영이다. \(\mathbf b\)가 단위벡터라면 \(\mathbf a\cdot\mathbf b\)는 정확히 '\(\mathbf a\)가 \(\mathbf b\) 방향으로 얼마나 뻗어 있는가'다. 벡터의 \(i\)번째 성분 \(a_i\) 자체도 \(\mathbf a\)와 \(i\)번째 축 단위벡터의 내적이다. 둘째, 내적은 유사도다. 방향이 같으면 크고, 직교하면 0, 반대면 음수다. 신경망의 뉴런 하나는 입력 벡터와 가중치 벡터의 내적을 계산하는 장치이고(2장), 어텐션의 점수 \(\mathbf q\cdot\mathbf k\)는 "이 질의(query)가 저 키(key)와 얼마나 맞는가"를 내적으로 잰 것이다(5장).
세 가지 '가까움': 내적, 코사인 유사도, 유클리드 거리
검색이나 추천에서 "가장 비슷한 것"을 고를 때 쓰는 척도는 보통 세 가지다.
| 척도 | 식 | 크기(노름)에 대한 반응 | 주로 쓰는 곳 |
|---|---|---|---|
| 내적 | \(\mathbf a\cdot\mathbf b\) | 길수록 유리 (인기·강도까지 반영) | 어텐션 점수, 행렬 분해 추천, MIPS 검색 |
| 코사인 유사도 | \(\dfrac{\mathbf a\cdot\mathbf b}{\lVert\mathbf a\rVert\lVert\mathbf b\rVert}\) | 무시 (방향만 본다) | 문장 임베딩 검색, RAG (10장) |
| 유클리드 거리 | \(\lVert\mathbf a-\mathbf b\rVert\) | 크기가 비슷해야 가깝다 | k-NN, 군집화, 이미지 특징 |
셋은 하나의 항등식으로 묶인다. 거리의 제곱을 전개하면
그래서 실무 벡터 DB는 임베딩을 미리 L2 정규화해 저장하고 내적만 계산하는 경우가 많다. 반대로 정규화하지 않으면 세 척도는 서로 다른 순위를 내놓는다. 내적은 '전반적으로 강한' 벡터를 선호하고(모든 장르 점수가 높은 대작), 거리는 '크기까지 비슷한' 벡터를 선호한다. 아래 시뮬레이터에서 직접 확인해 보자. 영화와 점수는 교육용으로 만든 작은 예시다.
취향 벡터 추천: 척도에 따라 순위가 바뀐다
순위 목록에서 영화를 누르면 그 영화의 벡터가 막대 위에 테두리로 겹쳐진다.
문장 임베딩의 길이는 문장 길이·단어 빈도 같은 '의미와 무관한' 요인에 영향을 받기 쉽다. 방향만 보는 코사인 유사도는 이런 잡음에 덜 민감하다. 반면 어텐션의 \(\mathbf q\cdot\mathbf k\)처럼 크기에도 정보가 실린 경우(확신이 클수록 큰 점수)에는 정규화하지 않은 내적을 쓰고, 대신 \(\sqrt{d}\)로 나눠 크기를 다스린다(5장).
행렬 = 선형 변환
행렬을 '숫자를 직사각형으로 늘어놓은 표'로만 보면 선형대수가 지루해진다. 더 좋은 관점은 행렬을 공간을 변형하는 함수로 보는 것이다. 2×2 행렬 \(A\)는 평면 위의 모든 점 \(\mathbf x\)를 새 점 \(A\mathbf x\)로 보낸다. 이 변환은 선형(linear)이다. 즉 \(A(\mathbf x+\mathbf y) = A\mathbf x + A\mathbf y\), \(A(c\mathbf x) = cA\mathbf x\)가 성립하며, 기하학적으로는 "격자선이 직선으로 남고, 평행하고 등간격이며, 원점이 고정되는" 변환이다.
선형성 덕분에 변환 전체가 단 두 벡터로 결정된다. 기저 벡터 \(\hat\imath=(1,0)\)과 \(\hat\jmath=(0,1)\)이 어디로 가는지만 알면, 임의의 \(\mathbf x = x_1\hat\imath + x_2\hat\jmath\)는 \(A\mathbf x = x_1(A\hat\imath) + x_2(A\hat\jmath)\)로 간다. 그리고 \(A\hat\imath\)는 \(A\)의 첫 번째 열, \(A\hat\jmath\)는 두 번째 열이다.
행렬식과 고유벡터
행렬식(determinant) \(\det A = ad - bc\)는 변환이 넓이를 몇 배로 바꾸는지를 나타낸다. 단위 정사각형이 넓이 \(|\det A|\)의 평행사변형이 되고, 모든 도형의 넓이가 같은 비율로 변한다. 부호가 음수면 공간이 뒤집힌다(거울상: \(\hat\imath\)에서 \(\hat\jmath\)로 도는 방향이 반시계에서 시계로 바뀜). \(\det A = 0\)이면 평면이 직선이나 점으로 찌그러져 정보가 사라지고, 되돌리는 역행렬이 존재하지 않는다.
고유벡터(eigenvector)는 변환 후에도 자기 방향을 유지하는 특별한 벡터다. \(A\mathbf v = \lambda\mathbf v\)를 만족하며, \(\lambda\)(고유값)는 그 방향으로 늘어나는 배율이다. 2×2에서 고유값은 \(\lambda^2 - (\mathrm{tr}A)\lambda + \det A = 0\)의 근이다. 회전처럼 모든 방향이 돌아가 버리는 변환은 실수 고유벡터가 없다. 고유벡터는 이 장 끝의 PCA에서 '데이터가 가장 넓게 퍼진 방향'으로 다시 등장한다.
2×2 변환 격자: 기저를 끌어 공간을 변형하기
행렬곱 = 변환의 합성 = 내적의 표
변환 \(B\)를 하고 나서 변환 \(A\)를 하면, 결과도 하나의 선형 변환이고 그 행렬이 행렬곱 \(AB\)다. 적용 순서는 오른쪽에서 왼쪽이다: \((AB)\mathbf x = A(B\mathbf x)\). 일반적으로 \(AB \ne BA\)다 — 회전하고 전단하는 것과 전단하고 회전하는 것은 다른 결과를 낳는다.
계산 관점에서 \(A\)가 \(m\times k\), \(B\)가 \(k\times n\)이면 \(C = AB\)는 \(m\times n\)이고, 각 성분은 \(A\)의 행과 \(B\)의 열의 내적이다. 안쪽 차원 \(k\)가 맞아야 곱할 수 있고, 곱하고 나면 그 차원은 '소모'되어 사라진다.
아래에서 작은 \(A(3\times4)\cdot B(4\times2)\)를 직접 계산해 보자. 결과 칸을 누르면 그 칸에 쓰인 행과 열이 강조되고, '단계 재생'은 곱셈-덧셈을 하나씩 수행하며 FLOPs를 센다.
행렬곱 단계 시각화: C = A · B
\((AB)C = A(BC)\)는 결과가 같지만 연산량은 다를 수 있다. \(A\)가 \(1\times 4096\), \(B\)가 \(4096\times4096\), \(C\)가 \(4096\times 4096\)이면 \((AB)C\)는 \(2\cdot4096^2\cdot2 \approx 67\)M FLOPs인데 \(A(BC)\)는 \(BC\)만으로 \(2\cdot4096^3\approx 137\)G FLOPs다. 2000배 차이다. LoRA(작은 행렬 두 개의 곱으로 가중치 변화를 표현)가 효율적인 이유도 이런 순서·랭크 계산에 있다.
신경망 층 = y = Wx + b, 그리고 텐서 shape
이제 신경망의 기본 부품을 선형대수로 쓸 수 있다. 완전연결층(fully-connected, Linear, Dense layer)은 입력 벡터 \(\mathbf x\in\mathbb R^{d_\text{in}}\)를 받아 다음을 계산한다.
여기에 비선형 활성화 함수를 끼워 쌓으면 신경망이 된다(2장). 실제로는 입력을 하나씩 넣지 않는다. 여러 샘플을 행으로 쌓은 행렬 \(X\in\mathbb R^{B\times d_\text{in}}\)를 한 번에 넣어 \(Y = XW^\top + \mathbf b\)를 계산한다. 이것이 배치(batch)다. 행렬-벡터 곱 여러 번이 행렬-행렬 곱 한 번이 되어, 가중치 \(W\)를 메모리에서 한 번 읽고 \(B\)번 재사용한다.
언어 모델에서는 차원이 하나 더 붙는다. 배치 \(B\)개의 문장 각각에 토큰이 \(T\)개 있고, 토큰마다 \(d\)차원 벡터가 있으니 데이터는 (B, T, d) 모양의 3차원 배열, 곧 텐서(tensor)다. Linear 층은 마지막 축에만 작용한다. 앞의 \(B\times T\)는 그냥 '독립적인 벡터 \(BT\)개'로 펼쳐서 하나의 큰 행렬곱 \((BT\times d)\cdot(d\times d_\text{out})\)으로 처리된다.
| 모델 (공개 수치) | d_model | FFN 차원 | 층 수 | 토큰당 대략 FLOPs (순전파) |
|---|---|---|---|---|
| GPT-2 small (2019) | 768 | 3,072 | 12 | 약 0.25 GFLOPs |
| Llama 3 8B (2024) | 4,096 | 14,336 | 32 | 약 16 GFLOPs |
표의 마지막 열은 '파라미터 수 × 2'라는 어림셈이다. 파라미터 하나당 토큰마다 곱셈-덧셈 한 번(2 FLOPs)을 하기 때문이다(GPT-2 small 약 124M, Llama 3 8B 약 8.03B 파라미터). 즉 LLM 추론 연산의 대부분은 행렬곱이고, 어텐션 점수 계산 \(QK^\top\)과 가중합 \(AV\)도 행렬곱이다(5장).
GPU는 행렬곱 기계다
행렬곱은 하드웨어 입장에서 이상적인 작업이다. 결과 칸들이 서로 독립이라 대규모 병렬화가 쉽고, 같은 데이터를 여러 번 재사용하므로 메모리에서 가져온 바이트당 연산이 많다. \(n\times n\) 행렬 두 개를 곱하면 데이터는 \(O(n^2)\)인데 연산은 \(O(n^3)\)이다. NVIDIA H100 SXM은 BF16 행렬곱 전용 유닛(Tensor Core)으로 약 989 TFLOPS(dense)를 내지만, 메모리 대역폭은 약 3.35 TB/s다. 바이트당 약 300 FLOPs 이상을 해야 연산기가 쉬지 않는다. 배치가 작은 LLM 디코딩은 이 조건을 못 채워 메모리 대역폭에 묶이는데, 이것이 8장의 KV 캐시와 11장 루프라인 분석의 출발점이다.
고차원의 직관: 거의 모든 것이 직교한다
2차원과 3차원에서 기른 직관은 768차원이나 4096차원에서 종종 틀린다. 가장 중요한 차이는 이것이다: 무작위로 뽑은 두 고차원 벡터는 거의 항상 거의 직교한다. 각 성분을 표준정규분포에서 독립으로 뽑은 두 벡터의 코사인 유사도는 평균 0, 분산 약 \(1/d\)인 분포를 따른다.
이 사실은 두 가지 귀결을 낳는다. 축복: 거의 직교하는 방향이 사실상 무한히 많다. \(d\)차원에는 정확히 직교하는 방향이 \(d\)개뿐이지만, 서로 코사인이 0.1 이하인 '거의 직교' 방향은 \(d\)에 대해 지수적으로 많이 넣을 수 있다. 그래서 4096차원 임베딩 하나에 수만 개의 개념을 서로 간섭 적게 담을 수 있다(이른바 superposition 가설). 또 무작위 투영으로 차원을 크게 줄여도 거리가 대략 보존된다(Johnson–Lindenstrauss 보조정리). 저주: 거리가 집중된다. 무작위 점들 사이의 거리가 모두 비슷해져서, '가장 가까운 이웃'과 '가장 먼 이웃'의 차이가 상대적으로 작아진다. 단순 거리 기반 방법이 고차원에서 어려워지는 이유다.
부피도 이상하게 행동한다. 반지름 1인 \(d\)차원 공의 부피는 \(r^d\)에 비례하므로, 바깥 두께 \(\varepsilon\)짜리 껍질이 차지하는 비율은 \(1-(1-\varepsilon)^d\)다. \(d=1000\), \(\varepsilon=0.01\)이면 99.996%다. 고차원 공의 부피는 거의 전부 표면 근처에 있다. 고차원 가우시안 표본도 원점 근처가 아니라 반지름 \(\sqrt d\) 근처의 얇은 껍질에 몰려 있다.
차원의 저주와 축복: 랜덤 벡터 실험
위: 무작위 벡터 쌍의 코사인 유사도 히스토그램(성분 ~ N(0,1), 매번 실제로 표본을 뽑는다). 아래: 공 부피 중 바깥 껍질의 비율 1−(1−ε)d.
노름, 단위벡터, 정규화
벡터의 '크기'를 재는 방법은 하나가 아니다. 일반적인 \(L_p\) 노름(norm)은 다음과 같다.
벡터를 자기 L2 노름으로 나누는 것을 L2 정규화(L2 normalization)라 한다. 결과는 단위 구 위의 점이고, 크기 정보는 버리고 방향만 남는다. 앞에서 본 대로 정규화된 벡터끼리는 내적 = 코사인 유사도다.
신경망 내부에서도 비슷한 정규화가 끊임없이 일어난다. 층을 수십 개 거치는 동안 벡터의 크기가 폭주하거나 소멸하지 않도록, Transformer는 각 블록 앞에서 토큰 벡터의 크기를 다시 맞춘다. Llama 계열이 쓰는 RMSNorm은 이름 그대로 성분의 제곱평균제곱근(RMS)으로 나눈다.
성분이 대략 표준정규분포인 \(d\)차원 벡터의 길이는 약 \(\sqrt d\)다. 768차원이면 약 27.7, 4096차원이면 약 64다. 그래서 두 벡터의 내적은 \(d\)가 클수록 표준편차 \(\sqrt d\)로 커진다. 어텐션이 \(\mathbf q\cdot\mathbf k\)를 \(\sqrt{d_k}\)로 나누는 이유가 바로 이것이다 — 나누지 않으면 소프트맥스가 한 토큰에만 몰린다.
투영과 차원 축소: PCA 맛보기
3차원 물체의 그림자는 2차원 평면으로의 투영(projection)이다. 서로 직교하는 단위벡터 \(\mathbf u_1,\mathbf u_2\)가 만드는 평면으로 점 \(\mathbf x\)를 투영하면 평면 위 좌표는 \((\mathbf u_1\cdot\mathbf x,\;\mathbf u_2\cdot\mathbf x)\)다. 이것을 행렬로 쓰면 \(U^\top\mathbf x\)이고(\(U=[\mathbf u_1\;\mathbf u_2]\)는 \(3\times2\)), 원래 공간 안의 그림자 위치는 \(P\mathbf x = UU^\top\mathbf x\)다. \(P\)를 투영 행렬이라 하며 \(P^2=P\)(두 번 투영해도 같다)를 만족한다. 앞의 변환 격자 시뮬레이터의 '투영' 프리셋이 2D 버전이었다.
그렇다면 데이터를 잘 보여 주는 '좋은 그림자'는 어느 방향일까? 정보를 가장 많이 남기는 투영은 투영 후에도 점들이 가장 넓게 퍼져 있는(분산이 큰) 방향이다. 데이터를 평균이 0이 되게 옮긴 뒤 공분산 행렬 \(C = \frac1n X^\top X\)를 구하면, 단위벡터 \(\mathbf u\) 방향으로 투영한 분산은 \(\mathbf u^\top C\mathbf u\)다. 이를 최대화하는 \(\mathbf u\)가 \(C\)의 가장 큰 고유값에 해당하는 고유벡터이고, 그 고유값이 곧 그 방향의 분산이다. 이것이 주성분 분석(PCA, Principal Component Analysis)이다.
3D 점구름을 2D로: 가장 좋은 그림자 찾기
화면 자체가 3D → 2D 투영이다. 점 색은 PC1 좌표(제1주성분 방향 위치).
PCA는 '선형' 차원 축소다. 투영은 행렬곱 하나이므로 빠르고 되돌리기도 쉽다(\(UU^\top\)로 복원). 신경망의 많은 부분도 이렇게 해석할 수 있다. 어텐션 헤드는 \(d\)차원 토큰 벡터를 \(d_k = d/h\)차원 부분공간으로 투영해서 그 안에서만 비교하고(5장), LoRA는 가중치 변화가 낮은 랭크 부분공간에 있다고 가정한다. 이후 장에서 "투영"이라는 말이 나오면 행렬곱으로 다른(대개 더 작은) 공간의 좌표를 구하는 것이라고 읽으면 된다.
핵심 정리
- 이미지·소리·단어·취향은 모두 고정 길이 숫자 배열, 곧 특징 공간의 점이 된다. 학습은 의미상 가까운 것이 기하학적으로 가까워지도록 공간을 만드는 일이다.
- 내적 \(\mathbf a\cdot\mathbf b = \sum a_ib_i = \lVert\mathbf a\rVert\lVert\mathbf b\rVert\cos\theta\)는 투영이자 유사도다. 정규화하면 내적 = 코사인이고 \(\lVert\mathbf a-\mathbf b\rVert^2 = 2-2\cos\theta\)라 세 척도의 순위가 같아진다.
- 행렬은 선형 변환이며 열은 기저 벡터의 도착지다. \(\det\)는 넓이 배율(음수면 뒤집힘, 0이면 정보 손실), 고유벡터는 방향이 유지되는 축이다.
- 행렬곱은 변환의 합성이고 각 칸은 행·열 내적이다. \((m\times k)(k\times n)\)의 비용은 2mnk FLOPs이며, LLM 연산의 대부분이 이것이다.
- 신경망 층은 \(\mathbf y = W\mathbf x+\mathbf b\)이고, 실제로는
(B, T, d)텐서의 마지막 축에 작용하는 큰 행렬곱이다. 데이터 재사용이 높은 행렬곱은 GPU Tensor Core에 이상적이다. - 고차원에서 랜덤 벡터는 거의 직교하며(\(\cos\)의 표준편차 \(\approx 1/\sqrt d\)), 거리는 집중되고 부피는 껍질에 몰린다.
- 노름은 크기, L2 정규화는 방향만 남기기, RMSNorm은 \(\sqrt d\)배 된 L2 정규화다. 투영은 행렬곱이고, PCA는 공분산의 고유벡터로 분산을 최대한 보존하는 투영을 찾는다.
확인 퀴즈
1. 벡터 b의 방향은 그대로 두고 길이만 3배로 늘렸다. a·b와 코사인 유사도는 각각 어떻게 되는가?
2. 행렬 \(A = \begin{bmatrix}2 & 1\\ 1 & -1\end{bmatrix}\)이 평면에 하는 일로 옳은 것은?
3. \((128\times512)\)와 \((512\times256)\) 행렬을 곱하는 데 드는 FLOPs는?
4. shape (32, 128, 768)인 텐서 X에 가중치 \(W\in\mathbb R^{3072\times768}\)인 Linear 층(\(XW^\top+\mathbf b\))을 적용한 결과의 shape은?
5. 성분이 독립 표준정규분포인 1024차원 랜덤 벡터 두 개의 코사인 유사도는 대략 어느 범위에 몰려 있는가?
6. 모든 임베딩을 L2 정규화해 두었다. 질의와의 '유클리드 거리가 작은 순'과 '코사인 유사도가 큰 순' 순위의 관계는?