履歴書の証明写真をAIで作る方法|規格の基本と注意点
AIタグが付けられた新着記事 - Qiita
就活の写真というと「スピード写真機に出向く」「写真屋で撮る」のが定番でしたが、最近は自撮り1枚からAIで証明写真を作るサービスが増えています。自分は就活支援AIツール( https://www.aijpjobs.com )を運営しており、証明写真生成ツール(自撮り1枚→約1...
Балл: 57.35Уверенность: 54%
ПодробнееЗагружаем каталог…
НАВИГАТОР ПО ВОЗМОЖНОСТЯМ ИИ
Найдите свой ИИ-инструмент. Бесплатный доступ, пробные периоды и кредиты — в одном месте.
AIタグが付けられた新着記事 - Qiita
就活の写真というと「スピード写真機に出向く」「写真屋で撮る」のが定番でしたが、最近は自撮り1枚からAIで証明写真を作るサービスが増えています。自分は就活支援AIツール( https://www.aijpjobs.com )を運営しており、証明写真生成ツール(自撮り1枚→約1...
Балл: 57.35Уверенность: 54%
Подробнее掘金
Apache Griffin(已退休)的历史价值在于,它较早把大数据质量问题抽象成平台化流程:规则定义、Spark 计算、指标沉淀、服务化管理和可视化展示。
Балл: 57.32Уверенность: 54%
Подробнее掘金
“For we can enormously extend the record; yet even in its present bulk we can hardly consult it.” “记
Балл: 57.31Уверенность: 54%
Подробнее掘金
2026 年 8 月,Anthropic 发布《The AI-native SDLC Playbook》。它讨论的不是模型能写多少代码,而是另一个更现实的问题:Agent 已经能在几小时内生成大量代码
Балл: 57.31Уверенность: 54%
Подробнееvelog
오늘의 핵심 오늘은 단순히 개념을 외우는 수준보다, 왜 이런 식이 나오는지 수식적으로 연결하는 것 에 집중했다. 흐름은 크게 다음과 같다. BCE → Mini-batch Gradient → 분산 → Xavier Initialization → Entropy → Cross Entropy → KL Divergence → Mutual Information 1. Binary Cross Entropy는 무엇을 보는가? 📌 Binary Classification의 출력 Binary Classification에서 실제 정답은 보통 $$ y \in {0,1} $$ 이다. 예를 들어 스팸 분류라면: $y=1$ : 스팸 $y=0$ : 스팸 아님 하지만 모델은 바로 0 또는 1 을 출력하는 것이 아니라, 보통 $$ p = P(y=1\mid x) $$ 즉 입력 $x$가 class 1일 확률 을 예측한다. 예를 들어: p = 0.8 이면 모델은 해당 입력이 class 1일 확률을 80%로 보고 있다는 뜻이다. 최종 class는 필요하다면 threshold를 적용해 결정한다. p >= 0.5 → class 1 p < 0.5 → class 0 📌 BCE의 목적 Binary Cross Entropy는 실제 정답 $y\in{0,1}$과 모델이 예측한 확률 $p\in[0,1]$이 얼마나 잘 맞는지 를 측정하는 Loss다. 수식은 다음과 같다. $$ L = -\left[ y\log p + (1-y)\log(1-p) \right] $$ 여기서: $y$ : 실제 정답 $p$ : 모델이 예측한 class 1의 확률 정답이 1이면: $$ L=-\log p $$ 정답이 0이면: $$ L=-\log(1-p) $$ 따라서 모델이 정답 클래스에 높은 확률을 줄수록 Loss가 작아진다. 2. Cross Entropy에서 BCE로 내려오기 일반적인 Cross Entropy는 $$ L = -\sum_{k=1}^{K} y_k \log p_k $$ 이다. Binary Classification은 class가 두 개뿐이므로 $K=2$라고 생각하면 된다. class 1의 확률을 $p$라고 하면 class 0의 확률은 $$ 1-p $$ 이고, 정답도 하나의 binary 값 $y$로 표현하면 $$ y_1=y,\qquad y_0=1-y $$ 가 된다. 따라서 그대로 대입하면 $$ L = -\left[ y\log p + (1-y)\log(1-p) \right] $$ 즉 BCE가 나온다. 💡 BCE는 Cross Entropy를 Binary Classification에 맞게 표현한 형태라고 볼 수 있다. 3. 왜 마지막 Activation으로 Sigmoid를 사용하는가? 📌 Linear 출력은 확률이 아니다 마지막 Linear Layer는 보통 $$ z = Wx+b $$ 를 출력한다. 이때 $z$의 범위는 $$ -\infty < z < \infty $$ 이므로 그대로는 확률로 해석할 수 없다. Sigmoid는 $$ \sigma(z) = \frac{1}{1+e^{-z}} $$ 이고 출력 범위는 $$ 0 < \sigma(z) < 1 $$ 이다. 즉: Linear ↓ logit z ↓ Sigmoid ↓ class 1일 확률 p ↓ BCELoss 의 흐름이 만들어진다. 📌 왜 ReLU는 마지막 Activation으로 부적절한가? ReLU는 $$ ReLU(z)=\max(0,z) $$ 이고 출력 범위는 $$ [0,\infty) $$ 이다. 하지만 BCE에서 $p$는 확률이므로 $$ 0 \le p \le 1 $$ 이어야 한다. 따라서 2 , 10 같은 값도 출력할 수 있는 ReLU는 BCE 앞의 최종 Activation으로 부적절하다. 🎯 BCE가 원하는 것은 class 1의 확률이고, ReLU는 확률 범위를 보장하지 않는다. 4. BCELoss와 BCEWithLogitsLoss 📌 BCELoss BCELoss 는 이미 확률로 변환된 값을 입력으로 받는다. model = nn.Sequential( nn.Linear(..., 1), nn.Sigmoid() ) criterion = nn.BCELoss() 즉: logit ↓ Sigmoid ↓ probability ↓ BCELoss 이다. 📌 BCEWithLogitsLoss PyTorch에서는 보통 다음 형태를 더 많이 사용한다. model = nn.Linear(..., 1) criterion = nn.BCEWithLogitsLoss() 이 경우 마지막에 Sigmoid() 를 직접 붙이지 않는다. BCEWithLogitsLoss 가 내부적으로 Sigmoid + BCE를 수치적으로 더 안정적인 방식으로 계산 하기 때문이다. 🚨 BCEWithLogitsLoss 를 사용할 때 마지막에 Sigmoid를 중복해서 넣으면 안 된다. 5. Mini-batch Gradient Descent 📌 GD와 SGD 복습 전체 데이터가 $N$개라고 하자. 전체 Loss는 $$ L(\theta) = \frac{1}{N} \sum_{i=1}^{N} L_i(\theta) $$ 이다. 여기서: $\theta$ : 모델의 모든 Parameter $L_i$ : i번째 데이터의 Loss Batch Gradient Descent는 전체 데이터를 사용한다. $$ g = \nabla_\theta L = \frac{1}{N} \sum_{i=1}^{N} \nabla_\theta L_i $$ Pure SGD는 데이터 하나만 사용한다. $$ g_i = \nabla_\theta L_i $$ 📌 Mini-batch는 둘의 중간 Batch Size가 $B$라면 $$ L_B = \frac{1}{B} \sum_{i\in \mathcal{B}} L_i $$ 를 계산한다. 그리고 $$ g_B = \nabla_\theta L_B $$ 를 사용한다. 미분은 선형이므로 $$ \nabla_\theta \left( \frac{1}{B} \sum_{i\in\mathcal{B}}L_i \right) = \frac{1}{B} \sum_{i\in\mathcal{B}} \nabla_\theta L_i $$ 이다. 각 데이터의 Gradient를 직접 하나씩 구해서 평균내는 것과, 평균 Loss를 한 번 Backward 하는 것은 같은 결과를 준다. 6. PyTorch에서는 평균 Loss를 통해 Gradient 평균이 만들어진다 PyTorch의 많은 Loss 함수는 기본적으로 reduction="mean" 을 사용한다. 예를 들어: criterion = nn.MSELoss() loss = criterion(y_hat, y) loss.backward() 이면: 각 데이터 Loss 계산 ↓ Batch 평균 Loss ↓ Backward ↓ 결과적으로 Batch Gradient의 평균 이 만들어진다. 7. Batch Size와 Gradient Variance Mini-batch Gradient를 $$ g_B = \frac{1}{B} \sum_{i=1}^{B}g_i $$ 라고 하자. 각 데이터의 Gradient가 서로 어느 정도 독립이라고 단순하게 가정하면 $$ Var(g_B) \approx \frac{Var(g_i)}{B} $$ 이므로 $$ Var(g_B)\propto\frac{1}{B} $$ 이다. 즉: Batch Size ↑ ↓ 더 많은 Gradient를 평균 ↓ 개별 데이터 때문에 튀는 정도 감소 ↓ Gradient Variance ↓ 💡 여기서 분산은 Mini-batch를 다르게 뽑았을 때 Gradient 추정값이 얼마나 흔들리는가 를 의미한다. 8. Mini-batch의 분산과 초기화의 분산은 다른 이야기다 Mini-batch에서는 $$ g_B = \frac{g_1+\cdots+g_B}{B} $$ 처럼 평균 을 낸다. 반면 초기화에서는 다음 Layer의 Pre-activation이 $$ z = w_1x_1+w_2x_2+\cdots+w_nx_n $$ 처럼 여러 값을 합 한다. 여기서: $x_i$ : 이전 Layer의 Activation $w_i$ : Weight $n$ : Input Feature 수, 즉 fan-in $z$ : Activation Function을 통과하기 전 값 9. 분산(Variance)이란? 분산은 값 자체가 크냐 작냐가 아니라 값들이 자신의 평균 주변에서 얼마나 퍼져 있는가 를 나타낸다. $$ Var(X) = E[(X-E[X])^2] $$ 예를 들어: [1000, 1000, 1000] 은 값은 크지만 모두 같으므로 분산은 0이다. 반면: [-1000, 0, 1000] 은 평균 주변에서 크게 퍼져 있으므로 분산이 크다. 📌 Scale과 분산 중요한 성질은 $$ Var(aX) = a^2Var(X) $$ 이다. 즉 Weight 전체의 Scale을 줄이면 Weight 분포의 분산도 자연스럽게 줄어든다. 10. 초기화에서 왜 fan-in이 커지면 Weight를 작게 해야 하는가? 다음 식을 생각하자. $$ z = \sum_{i=1}^{n}w_ix_i $$ 일반적으로는 $$ Var(z) = \sum_iVar(w_ix_i) + 2\sum_{i<j}Cov(w_ix_i,w_jx_j) $$ 이다. 초기화 이론에서는 단순화를 위해 다음과 같은 가정을 둔다. Weight들이 서로 독립 입력 Activation들도 대략 비상관 평균이 0에 가까움 $w_i$와 $x_i$가 독립 그러면 Covariance 항을 무시할 수 있고 $$ Var(z) \approx \sum_iVar(w_ix_i) $$ 가 된다. 또한 $$ Var(w_ix_i) \approx Var(w_i)Var(x_i) $$ 라고 두면 $$ \boxed{ Var(z) \approx nVar(w)Var(x) } $$ 가 된다. 즉: fan-in n ↑ ↓ 더 많은 w_i x_i 항의 분산이 더해짐 ↓ Var(z) ↑ 가능 ↓ Weight Scale / Var(w)를 줄일 필요가 있음 11. 좋은 초기화의 핵심 좋은 초기화에는 두 가지 목표가 있다. 📌 1. Symmetry Breaking 모든 Weight가 동일하면 뉴런들이 같은 Feature를 학습할 수 있다. 따라서 Weight들이 서로 다른 값을 가져야 한다. 📌 2. 적절한 Scale Weight가 너무 크면 Activation과 Gradient가 불안정해질 수 있고, 너무 작으면 Signal이 지나치게 작아질 수 있다. 🎯 좋은 초기화 = 서로 다른 Weight + 적절한 Weight Scale 12. Xavier Initialization 📌 Forward 관점 Forward에서 $$ Var(z) \approx fan_{in}Var(w)Var(x) $$ 이다. Layer를 지나도 Activation의 분산을 대략 유지하고 싶다면 $$ Var(z) \approx Var(x) $$ 를 목표로 둘 수 있다. 따라서 $$ fan_{in}Var(w)Var(x) \approx Var(x) $$ 이고 $Var(x)\neq0$라면 $$ Var(w) \approx \frac{1}{fan_{in}} $$ 이 된다. 📌 Backward 관점 Backward에서는 대략 $$ Var(\delta_{prev}) \approx fan_{out}Var(w)Var(\delta_{next}) $$ 라고 볼 수 있다. Gradient의 분산을 유지하려면 $$ Var(w) \approx \frac{1}{fan_{out}} $$ 이 필요하다. Forward와 Backward의 요구가 다를 수 있으므로 Xavier는 둘을 절충해 $$ \boxed{ Var(w) = \frac{2}{fan_{in}+fan_{out}} } $$ 를 사용한다. 13. Xavier Normal Xavier Normal은 $$ w \sim \mathcal{N} \left( 0, \frac{2}{fan_{in}+fan_{out}} \right) $$ 이다. 표준편차는 $$ std = \sqrt{ \frac{2}{fan_{in}+fan_{out}} } $$ 이다. PyTorch: nn.init.xavier_normal_(model.weight) 14. Xavier Uniform Uniform Distribution에서 $$ w\sim U(-a,a) $$ 이면 $$ Var(w)=\frac{a^2}{3} $$ 이다. Xavier의 목표 분산과 맞추면 $$ \frac{a^2}{3} = \frac{2}{fan_{in}+fan_{out}} $$ 이므로 $$ a = \sqrt{ \frac{6}{fan_{in}+fan_{out}} } $$ 이다. 즉 $$ w \sim U\left( -\sqrt{\frac{6}{fan_{in}+fan_{out}}}, +\sqrt{\frac{6}{fan_{in}+fan_{out}}} \right) $$ 이다. PyTorch: nn.init.xavier_uniform_(model.weight) 15. Xavier는 학습 내내 분산을 유지하는 방법인가? 아니다. Xavier는 학습 시작 시 Activation과 Gradient가 너무 커지거나 작아지지 않도록 안정적인 초기 조건을 만들어주는 초기화 방법 이다. 학습이 시작되면 $$ W \leftarrow W - \eta\nabla_WL $$ 에 의해 Weight가 계속 바뀐다. 따라서 Xavier가 처음 설정한 분산이 학습 내내 유지된다는 보장은 없다. 또 Xavier의 유도에는 독립성, 평균 0, 비슷한 분산 등의 가정이 들어가므로 실제 네트워크에서 정확히 분산이 보존되는 것도 아니다. 💡 Xavier의 목적은 학습 가능한 좋은 출발점 을 만드는 것이다. 16. Entropy — 정보 하나의 정보량 어떤 사건 $x$가 발생할 확률이 $p(x)$라고 하자. 그 사건의 정보량(Self-information)은 $$ I(x) = -\log_2p(x) $$ 로 정의한다. 확률이 높은 사건은 자주 발생하므로 정보량이 작고, 확률이 낮은 사건은 드물기 때문에 정보량이 크다. 예를 들어: $$ p(x)=\frac12 $$ 이면 $$ I(x) = -\log_2\frac12 = 1\text{ bit} $$ 이다. 또 $$ p(x)=\frac18 $$ 이면 $$ I(x) = 3\text{ bits} $$ 이다. 17. 왜 로그 밑이 2인가? Binary Code 관점에서 정보를 표현한다면 밑이 2인 로그를 사용한다. $$ I(x) = -\log_2p(x) $$ 이 경우 단위는 bit이다. 로그 밑을 $e$로 사용하면 단위는 nat이다. 💡 로그 밑이 2인 이유는 정보가 반드시 이진이라서가 아니라, 이진 부호화 관점에서 정보량의 단위를 bit로 표현하기 위해서 다. 18. Entropy Entropy는 각 사건의 정보량을 확률적으로 평균낸 값이다. $$ \boxed{ H(X) = -\sum_xp(x)\log_2p(x) } $$ 또는 기대값으로 $$ H(X) = E_{x\sim P} [-\log_2p(x)] $$ 라고 쓸 수 있다. 즉: Entropy는 실제 확률분포 $P$를 알고 있을 때, 그 분포에서 발생하는 데이터를 표현하는 데 필요한 평균 정보량이다. 정보이론적으로는 긴 symbol sequence를 이상적으로 부호화할 때 얻을 수 있는 최적 평균 부호 길이의 이론적 하한과 연결되는 값 이다. 🚨 Entropy와 정확히 같은 길이의 단일 symbol code가 항상 존재한다는 뜻은 아니다. 긴 sequence를 효율적으로 부호화할수록 평균 code length를 Entropy에 가깝게 만들 수 있다. 19. Cross Entropy 실제 데이터 분포를 $P$라고 하고, 우리가 모델링한 예측 분포를 $Q$라고 하자. Cross Entropy는 $$ \boxed{ H(P,Q) = -\sum_xp(x)\log_2q(x) } $$ 또는 $$ H(P,Q) = E_{x\sim P} [-\log_2q(x)] $$ 이다. 중요한 점은: 실제 데이터는 $P$에서 발생 정보량 계산에는 $Q$를 사용 한다는 것이다. 즉: 실제 데이터가 $P$를 따르는데, 우리가 $Q$라는 확률 모델을 사용해 데이터를 표현할 때 필요한 기대 정보량 이라고 볼 수 있다. 20. Entropy와 Cross Entropy의 차이 Entropy: $$ H(P) = -\sum_xp(x)\log_2p(x) $$ Cross Entropy: $$ H(P,Q) = -\sum_xp(x)\log_2q(x) $$ 즉: Entropy : 실제 분포 P를 사용 Cross Entropy : 실제 데이터는 P지만 예측 분포 Q를 사용 모델이 실제 분포를 정확히 맞히면 $$ Q=P $$ 이므로 $$ H(P,Q)=H(P) $$ 가 된다. 21. KL Divergence Cross Entropy와 Entropy의 차이가 KL Divergence다. $$ \boxed{ D_{KL}(P\parallel Q) = H(P,Q)-H(P) } $$ 이를 풀어쓰면 $$ D_{KL}(P\parallel Q) = \sum_x p(x) \log_2 \frac{p(x)}{q(x)} $$ 이다. 📌 의미 Entropy $H(P)$는 실제 분포를 알고 있을 때의 최적 기대 정보량이고, Cross Entropy $H(P,Q)$는 $Q$를 사용했을 때의 기대 정보량이다. 따라서 $$ H(P,Q)-H(P) $$ 는 잘못되거나 불완전한 분포 $Q$를 사용해서 생기는 추가적인 기대 정보 비용 으로 해석할 수 있다. 즉: 최적 기대 비용 H(P) + 분포를 잘못 가정해서 생긴 추가 비용 D_KL(P||Q) = 실제 기대 비용 H(P,Q) 이다. 📌 중요한 성질 $$ D_{KL}(P\parallel Q)\ge0 $$ 이고, 두 분포가 같으면 $$ D_{KL}(P\parallel Q)=0 $$ 이다. 하지만 일반적인 거리함수와 달리 $$ D_{KL}(P\parallel Q) \neq D_{KL}(Q\parallel P) $$ 일 수 있다. 즉 KL Divergence는 대칭적이지 않다. 22. Cross Entropy Loss와 KL Divergence의 관계 실제 데이터 분포 $P$가 고정되어 있다면 $$ H(P) $$ 는 모델 Parameter와 무관한 상수다. 그리고 $$ H(P,Q) = H(P) + D_{KL}(P\parallel Q) $$ 이므로 Cross Entropy를 최소화하는 것은 결국 $$ D_{KL}(P\parallel Q) $$ 를 최소화하는 것과 같다. 즉: Cross Entropy Loss를 줄인다는 것은 모델의 예측 분포 $Q$를 실제 데이터 분포 $P$에 가깝게 만드는 것이라고 볼 수 있다. 23. Mutual Information 이제 KL Divergence를 이용해 두 변수 $X$, $Y$가 얼마나 서로 의존하는지를 측정할 수 있다. 두 변수가 독립이라면 $$ P(X,Y) = P(X)P(Y) $$ 이다. 따라서 다음 두 분포를 비교하면 된다. 실제 Joint Distribution: $$ P_{XY}(x,y) $$ 독립이라고 가정했을 때의 분포: $$ P_X(x)P_Y(y) $$ 이 둘의 차이를 KL Divergence로 측정한 것이 Mutual Information이다. $$ \boxed{ I(X;Y) = D_{KL} \left( P_{XY} \parallel P_XP_Y \right) } $$ 24. Mutual Information의 수식 풀어쓰면 $$ \boxed{ I(X;Y) = \sum_{x,y} p(x,y) \log \frac{p(x,y)}{p(x)p(y)} } $$ 이다. 📌 독립이면? 독립이라면 $$ p(x,y)=p(x)p(y) $$ 이므로 $$ \frac{p(x,y)}{p(x)p(y)}=1 $$ 이고 $$ \log1=0 $$ 이다. 따라서 $$ I(X;Y)=0 $$ 이다. 즉: Mutual Information이 0이면 두 변수는 독립이다. 📌 의존성이 강하면? $X$를 알았을 때 $Y$에 대한 불확실성이 크게 줄어든다면 Mutual Information이 커진다. 따라서: Mutual Information은 $X$와 $Y$가 서로 공유하는 정보의 양 이라고 해석할 수 있다. 25. Mutual Information과 Entropy의 관계 Mutual Information은 다음과 같이도 쓸 수 있다. $$ \boxed{ I(X;Y) = H(X)+H(Y)-H(X,Y) } $$ 또는 $$ \boxed{ I(X;Y) = H(X)-H(X\mid Y) } $$ 이며 $$ I(X;Y) = H(Y)-H(Y\mid X) $$ 이기도 하다. 여기서 $H(X)$는 원래 $X$에 대한 불확실성이고,
velog
브랜드의 메시지를 디지털 채널에 최적화된 비주얼로 전달할 수 있어야 합니다. 강력한 이미지 편집 툴인 포토샵(Photoshop)을 활용해 브랜드의 스토리를 담은 3장짜리 카드뉴스를 제작해 봅니다. 1. 분석 및 계획 수립 카드뉴스 주제 및 브랜드 선정 (예: 새로 출시된 텀블러 홍보, 브랜드의 친환경 가치관 소개 등) 구성 요소 기획: 1장 (표지), 2장 (내용), 3장 (엔딩) 의도 파악 및 톤앤매너 설정 [예시 참고] 2. 카드뉴스 제작의 원칙 가독성 최우선: 텍스트가 배경 이미지에 묻히지 않도록 글자 뒤에 어두운 반투명 사각형을 깔거나 이미지의 밝기를 조절 시각적 일관성: 폰트, 정렬 방식, 핵심 컬러, 레이아웃 분위기가 하나로 이어져야 "하나의 브랜드가 만든 콘텐츠"로 인식 3. 실습 포토샵을 켜고 디지털 화면(인스타그램 등)에 최적화된 정사각형 해상도 (1080*1080px, 72ppi)로 새 대지를 만듭니다. 기획한 내용에 맞는 고화질 무료 이미지(Unsplash 등 활용)를 불러와 배치하고, 텍스트 도구(T)를 사용해 타이포그래피 위계를 잡아가며 글씨를 씁니다. 1장(표지)을 완성한 뒤, 레이아웃 구조를 유지한 채 내용만 바꾸어 2장(내용)과 3장(엔딩)을 차례로 제작합니다. 완성된 이미지는 File > Export > Save for Web 을 통해 JPEG 파일로 저장합니다. 느낀 점 칼럼에는 포토샵 7일 체험판이 있다고 나와있지만 1시간 동안 찾지를 못한 관계로 모바일 무려 판을 사용했는데 역시 컴퓨터와는 차이가 있을뿐더러 터치 인식이나 여러 가지 고난을 겪었다. 본 캠프에서 제대로 된 연습이 필요할 것 같다.
Балл: 54.4Уверенность: 49%
Подробнееvelog
[Backend] Uvicorn, FastAPI, Nginx의 역할 분담과 비동기(ASGI)의 비밀 (feat. C#과의 차이점) 웹 백엔드 아키텍처를 공부하다 보면 꼭 마주치는 조합이 있습니다. 바로 Nginx + Uvicorn + FastAPI입니다. "요청을 받아서 처리한다"는 점에서 다 비슷해 보이지만, 실제 내부를 들여다보면 각자의 역할이 명확히 나뉘어 있습니다. 이번 글에서는 이 세 가지 기술의 관계를 현실적인 비유로 알아보고, 많은 개발자들이 헷갈려하는 파이썬 비동기(ASGI)의 스레드 모델을 C#과의 비교를 통해 완벽하게 정리해 보겠습니다. 1. 한눈에 보는 역할 분담 (식당 비유 🏢) 이들의 관계는 '대형 종합 식당'의 운영 구조와 완전히 같습니다. Nginx (종합 안내 데스크 / 문지기): 세상 모든 사람(인터넷 외부)을 상대하며, 입구에서 자물쇠(HTTPS)를 풀어주고 손님을 적절한 구역으로 안내합니다. Uvicorn (고성능 서빙 로봇 / ASGI 서버): Nginx가 들여보낸 손님의 주문을 받아서 주방에 빛의 속도로 전달하고, 요리가 나오면 손님에게 배달합니다. 파이썬 코드를 읽을 줄 아는 통역사입니다. FastAPI (주방의 메인 셰프 / 웹 프레임워크): Uvicorn이 준 주문서(요청)를 바탕으로 데이터베이스를 뒤지거나 비즈니스 로직을 수행하여 진짜 요리(결과 데이터)를 만들어냅니다. 2. 현실적인 대작전 시나리오: 아이돌 콘서트 티켓팅 오픈! 🎬 사용자 1만 명이 동시에 예매 버튼을 눌렀을 때 내부에서 일어나는 실제 흐름입니다. 1단계: Nginx의 철통 방어 (HTTPS & 로드밸런싱) HTTPS 복호화: 손님들이 보낸 요청은 암호화(HTTPS)되어 있습니다. Nginx는 맨 앞에서 이 자물쇠를 번개 같은 속도로 풀어냅니다. (뒤쪽 엔진들의 연산 부담을 줄여줌) 로드밸런싱: Nginx 뒤에 Uvicorn 엔진 3대(A, B, C 포트)를 대기시켜 두고, 1만 명의 요청을 골고루 쪼개서 분배합니다. (Reverse Proxy 역할) 2단계: Uvicorn의 ASGI 비동기 마법 (초고속 접수) 구식 서버(WSGI)는 앞 손님의 요리가 끝날 때까지 뒤 손님을 대기시킵니다. 반면 Uvicorn(ASGI)은 1번 손님의 주문을 주방(FastAPI)에 던진 후, 주방이 데이터베이스를 조회하느라 멈춰 있는 시간(약 0.5초)을 가만히 기다리지 않습니다. "어, 1번 손님 조회 중이야? 오케이, 그럼 난 그 사이에 2번, 3번, 4번 주문 계속 받을게!" 하며 요청을 멈춤 없이 접수합니다. 3단계: FastAPI의 영리한 일 처리 (비동기 셰프) FastAPI 코드가 async def로 짜여 있다면, 셰프 역시 멀티태스킹의 신이 됩니다. 1번 주문의 DB 응답을 기다리는 동안 2번 주문을 처리하고, 1번 DB 결과가 도착하면 하던 걸 잠깐 멈추고 1번 요리를 완성해 Uvicorn에게 던집니다. 컴퓨터 세계에서 가장 느린 'I/O 대기 시간(DB 조회, 외부 API 호출)'을 완전히 재활용하는 구조입니다. 3. 라우팅 이후, 실제 코드는 어떤 스레드가 처리할까? 🤔 FastAPI가 내부적으로 스레드를 어떻게 쓰느냐는 개발자가 코드를 어떻게 짰느냐(async def vs def)에 따라 완전히 달라집니다. 1 async def로 짰을 때: 멀티태스킹의 신, 싱글 스레드 @app.get("/ticket")async def book_ticket(): await db.check_seat() # DB를 기다리는 동안 스레드는 다른 일을 하러 감 return {"status": "success"} 동작 방식: 별도의 프로세스나 스레드가 새로 생성되지 않습니다. Uvicorn의 단 하나의 메인 스레드 위에서 돕니다. 원리: await를 만나는 순간, 메인 스레드는 작업을 옆으로 치워두고 즉시 다음 손님의 요청을 처리합니다. 이벤트 루프(Event Loop)가 완료 신호를 주면 다시 돌아와 마무리합니다. (Context Switching 비용 최소화) 2 그냥 def로 짰을 때: 스레드 풀(Thread Pool)의 도우미들 @app.get("/heavy-job")def heavy_job(): time.sleep(2) # 메인 스레드를 얼려버리는 Blocking 코드 return {"status": "done"} 동작 방식: 파이썬에서 일반 def 코드가 멈추면 메인 스레드 전체가 얼어버립니다(Blocking). 원리: 이를 막기 위해 FastAPI는 내부적으로 '스레드 풀(Thread Pool)'이라는 서브 스레드(임시 직원)를 깨워 일을 넘깁니다. 메인 스레드는 자유 몸이 되어 다음 손님을 받고, 서브 스레드가 백그라운드에서 일을 처리합니다. 💡 참고 (Process는 언제 쓰나요?): 파이썬은 GIL(Global Interpreter Lock) 제약 때문에 하나의 프로세스 내에서 멀티 스레드를 써도 CPU 연산을 동시에 할 수 없습니다. 따라서 멀티코어 CPU를 100% 쓰려면 uvicorn main:app --workers 4처럼 Uvicorn 프로세스 자체를 여러 개 띄우고, 맨 앞의 Nginx가 이 프로세스들에게 요청을 분산해야 합니다. 4. ⚠️ 닷넷(C#) 개발자가 가장 흔하게 하는 착각! (C# vs Python 비동기) C# 개발자가 파이썬 FastAPI 생태계에 오면 엄청난 문화 충격(Culture Shock)을 겪습니다. 이름은 같은 async/await인데 스레드가 작동하는 방식이 정반대이기 때문입니다. 항목 C# (Task 기반 비동기) 🖥️ Python FastAPI (이벤트 루프) 🐍 기본 사상 멀티 스레드 기반 (스레드가 기본적으로 많음) 싱글 스레드 기반 (스레드가 기본적으로 1개) async 안 썼을 때 메인 스레드가 그대로 멈춤 (Blocking) FastAPI가 스레드 풀에 일을 넘겨 메인 스레드를 살림 async 썼을 때 await 시 스레드 풀의 다른 스레드로 작업이 넘어가서 실행됨 Microsoft Learn await 시 동일한 메인 스레드를 유지하며, 대기 시간 동안 눈알만 딴 데 돌려 다른 요청을 받음 C#에서의 비동기: 기본적으로 풍부한 멀티 스레드 풀을 활용합니다. 비동기를 써야 스레드 풀의 다른 직원에게 효율적으로 일을 넘기며(Task) 스레드를 나눠 씁니다 Microsoft Learn. FastAPI에서의 비동기: GIL 제약 때문에 "스레드 1개만 지독하게 굴리자"는 싱글 스레드 사상(Node.js와 유사)입니다. 비동기(async)를 써야 메인 스레드 하나로 광속 멀티태스킹이 일어나고, 비동기를 안 쓰면(def) 메인 스레드가 죽는 걸 막으려고 프레임워크가 억지로 스레드 풀을 켜서 유배를 보내는 구조입니다. 📝 총정리 및 결론 Nginx는 문앞에서 포트를 분배하고 암호(HTTPS)를 풀어주는 총괄 매니저입니다. Uvicorn은 Nginx에게 받은 요청을 파이썬 객체로 번역하는 비동기 엔진입니다. FastAPI에서 async def를 쓰면 단 하나의 메인 스레드가 징검다리 건너듯 비동기로 모든 요청을 광속 처리합니다. FastAPI에서 def를 쓰면 메인 스레드가 멈추는 걸 방지하기 위해 내부 스레드 풀(서브 스레드)로 작업을 토스합니다.
velog
결론부터 말하면 이렇다. 보안 취약점 시연 사이트를 만들 때 진짜 어려운 건 XSS나 SQL Injection 코드를 짜는 게 아니었다. 진짜 벽은 두 개였다. "해킹"이라는 표현을 AI 안전 분류기가 어떻게 받아들이는지, 그리고 더블클릭으로 여는 정적 파일이 브라우저 안에서 얼마나 할 수 있는 게 없는지. 상황 사내 바이브코딩 강의에서 실습으로 보여줄 사이트를 만들어야 했다. AI로 코드를 짤 때 흔히 딸려오는 취약점 4종 — XSS, SQL Injection, 파일 업로드, Rate limit(DDoS) — 을 눈으로 보여주는 사이트다. 구성은 단순했다. 한쪽엔 "사용자가 쓰는 화면", 다른 쪽엔 "그로 인해 뭐가 새는지 보는 화면". 왼쪽에서 공격하면 오른쪽에 결과가 뜨는 연출이 핵심이었다. 첫 번째 벽 — 말 한마디에 응답이 끊긴다 안전 분류기 : AI 응답이 위험한 요청인지 판단해서 생성 자체를 막는 필터링 단계. 초기 요청에 "해킹", "SQL Injection", "악성 스크립트 업로드" 같은 말을 그대로 섞어 썼더니 두 번 연속 응답이 끊겼다. 문제는 단어가 아니라 프레이밍이었다. "공격 도구를 만들어달라"로 읽히면 막히고, "취약한 코드 때문에 뭐가 새는지 보여주는 관찰 대시보드를 만들어달라"로 읽히면 통과됐다. 목적은 똑같은데 표현만 바꿨을 뿐이다. 이후로는 이 프레임을 설계 전체에 그대로 유지했다 — 공격 재현이 아니라 관찰. 두 번째 벽 — 화면을 나누면 왜 서로 못 알아볼까 프레이밍 문제를 풀고 나니 구조 문제가 남았다. 처음엔 "사용자 화면"과 "해커 화면"을 각각 별도 HTML 파일로 만들려 했다. 그런데 배포 형태가 서버 없이 더블클릭으로 여는 file:// 방식이라는 걸 다시 확인하고 나서 멈췄다. file:// 로 열린 두 개의 HTML 파일은 브라우저 보안 정책상 서로 다른 출처(origin)로 취급된다. localStorage도 BroadcastChannel도 탭 사이에서 공유되지 않거나 불안정하게 동작한다. "왼쪽에서 입력하면 오른쪽에 바로 뜬다"는 연출 자체가 깨질 수 있는 구조였다. 해결 — 파일을 하나로 합치니 문제 자체가 사라졌다 프레임워크 없이 단일 HTML 파일 안에 좌우 분할 레이아웃을 두는 쪽으로 바꿨다. 파일을 하나로 합치면 두 화면이 같은 문서, 같은 메모리 안의 상태를 공유하니 탭 간 통신 문제 자체가 사라진다. 별도 동기화 로직을 설계할 필요가 없어졌다. 랩은 4개 — XSS, SQL Injection, 파일 업로드, Rate limit — 각각에 취약 모드/안전 모드 토글을 붙였다. 업로드 랩에는 사진처럼 보이지만 열면 스크립트가 실행되는 예시 파일을 직접 만들어서, 취약 모드에서 실제로 실행되는 걸 눈으로 보여줬다. Rate limit(DDoS)은 원래 이런 점검 항목에서 곧잘 빠지는 축에 든다. 그래도 "커맨드 한 줄로 보안 점검을 돌리면 안 보고 지나가는 자리"라는 이유로 넣기로 했다. 눈에 보이지 않는 것도 눈으로 보여줘야 실습이 된다는 판단이었다. 트레이드오프 취약/안전 토글은 코드와 연출 분량이 두 배가 된다. 그래도 "지적은 AI가 하고, 고칠지는 사람이 정한다"는 메시지의 핵심이라 그대로 뒀다. 처음엔 코드부터 보여주는 구성이었는데, 비개발자 피드백을 받고 나서 뒤집었다. "이렇게 뚫렸어요 / 이렇게 막아요" 카드를 앞에 두고, 코드는 접어서 옵션으로만 남겼다. 애니메이션(유출 빔, 공격 성공 배너, 단계별 따라하기)은 스크린샷 정적 캡처로는 타이밍 확인이 안 됐다. 화면을 눈으로 보는 대신 상태값을 프로그램적으로 확인하는 방식으로 검증 방법 자체를 바꿨다. file:// 환경에서는 브라우저 확장 권한도 막히기 때문에, 검증은 로컬 서버를 별도로 띄워서 했다. 배포는 file:// , 검증은 서버 — 이 둘을 분리해야 했다. 교훈 보안·해킹을 다루는 교육 콘텐츠는 "방법"이 아니라 "결과를 관찰하는 대시보드"로 프레이밍해야 안전 분류기도, 청중의 이해도 통과한다. 그리고 file:// 로 배포해야 하는 정적 페이지는 탭 간 상태 공유가 막힌다는 전제를 깔고, 별도 파일보다 단일 파일 내 분할 UI를 먼저 고려하는 게 맞다. 코드 문제가 아니라 구조 문제였다. 그리고 그 구조 문제는 배포 방식 하나를 먼저 확인했으면 처음부터 안 생겼을 문제였다.
Балл: 54.4Уверенность: 49%
Подробнееvelog
“좋은 인연은 자주 만나는 것보다, 잊지 않고 연락하는 데서 이어진다.”
Балл: 54.4Уверенность: 49%
Подробнееvelog
🔎 문제 설명 AI 엔지니어인 현식이는 데이터를 분석하는 작업을 진행하고 있습니다. 데이터는 코드 번호(code), 제조일(date), 최대 수량(maximum), 현재 수량(remain)으로 구성되어 있으며 현식이는 이 데이터들 중 조건을 만족하는 데이터만 뽑아서 정렬하려 합니다. 예를 들어 다음과 같이 데이터가 주어진다면 data = [[1, 20300104, 100, 80], [2, 20300804, 847, 37], [3, 20300401, 10, 8]] 이 데이터는 다음 표처럼 나타낼 수 있습니다. code date maximum remain 1 20300104 100 80 2 20300804 847 37 3 20300401 10 8 주어진 데이터 중 제조일이 20300501 이전인 물건들을 현재 수량이 적은 순서로 정렬해야 한다면 조건에 맞게 가공된 데이터는 다음과 같습니다. data = [[3, 20300401, 10, 8], [1, 20300104, 100, 80]] 정렬한 데이터들이 담긴 이차원 정수 리스트 data와 어떤 정보를 기준으로 데이터를 뽑아낼지를 의미하는 문자열 ext, 뽑아낼 정보의 기준값을 나타내는 정수 val_ext, 정보를 정렬할 기준이 되는 문자열 sort_by가 주어집니다. data에서 ext 값이 val_ext보다 작은 데이터만 뽑은 후, sort_by에 해당하는 값을 기준으로 오름차순으로 정렬하여 return 하도록 solution 함수를 완성해 주세요. 단, 조건을 만족하는 데이터는 항상 한 개 이상 존재합니다. 제한사항 1 ≤ data의 길이 ≤ 500 data[i] 의 원소는 [코드 번호(code), 제조일(date), 최대 수량(maximum), 현재 수량(remain)] 형태입니다. 1 ≤ 코드 번호 ≤ 100,000 20000101 ≤ 제조일 ≤ 29991231 data[i][1] 은 yyyymmdd 형태의 값을 가지며, 올바른 날짜만 주어집니다. 1 ≤ 최대 수량 ≤ 10,000 1 ≤ 현재 수량 ≤ 최대 수량 ext 와 sort_by 의 값은 다음 중 하나를 가집니다. "code" "date" "maximum" "remain" 순서대로 코드 번호, 제조일, 최대 수량, 현재 수량을 의미합니다. val_ext 는 ext 에 따라 올바른 범위의 숫자로 주어집니다. 정렬 기준에 해당하는 값이 서로 같은 경우는 없습니다. 입출력 예 data = [[1, 20300104, 100, 80], [2, 20300804, 847, 37], [3, 20300401, 10, 8]] ext = "date" val_ext = 20300501 sort_by = "remain" result = [[3, 20300401, 10, 8], [1, 20300104, 100, 80]] 💡 코드 풀이 핵심 로직 1 문자열로 주어진 열 이름을 실제 인덱스로 바꾸기 2 ext 열의 값이 val_ext 보다 작은 행만 남기기 3 sort_by 열 기준으로 오름차순 정렬하기 🧐 정답 def solution(data, ext, val_ext, sort_by): index = { 'code' : 0, 'date' : 1, 'maximum' : 2, 'remain' : 3, } answer = [row for row in data if row[index[ext]] < val_ext] answer.sort(key=lambda row: row[index[sort_by]]) ✏️ 내가 놓친 것 문자열로 주어진 ext , sort_by 를 실제 데이터의 열 인덱스와 연결하는 방법을 떠올리지 못했다. 이 문제처럼 열 이름이 문자열로 주어지고, 각 열의 위치가 정해져 있는 경우에는 딕셔너리를 사용해 "열 이름": 인덱스 형태로 매핑하면 쉽게 처리할 수 있다. 다음에 비슷한 문제가 나오면 문자열 조건을 바로 비교하려 하지 말고, 먼저 딕셔너리를 이용해 실제 인덱스로 변환할 수 있는지 생각해보기.
Балл: 54.4Уверенность: 49%
Подробнееvelog
Jupyter, Kernel, ipykernel, 가상환경과 Python Interpreter 이해하기 Python으로 데이터 분석이나 AI를 시작하면 생각보다 자주 마주치는 단어들이 존재함. .py .ipynb Jupyter Kernel ipykernel Python Interpreter 가상환경 라이브러리 Interactive Window 처음에는 전부 비슷해 보이지만 실제로는 각각 담당하는 역할이 다름 . 이번 글에서는 단순히 사용 방법만 외우는 것이 아니라 다음 질문을 하나씩 해결하면서 전체 구조를 이해하는 것이 목적임. "내가 VS Code에서 작성한 Python 코드는 도대체 누가 실행하는 것일까?" 1. Jupyter란 무엇일까? ❓ 질문 Jupyter Notebook에서 말하는 Jupyter 는 무슨 뜻일까? Jupyter의 의미 Jupyter는 대화형 컴퓨팅 환경을 제공하는 프로젝트임. 이름은 전통적으로 다음 세 언어에서 유래한 것으로 설명됨. Ju → Julia Py → Python R → R 즉, Jupyter가 처음부터 Python만을 위한 프로그램은 아니라는 의미 . Python뿐만 아니라 해당 언어를 실행할 수 있는 Kernel만 존재한다면 다양한 언어를 Notebook 환경에서 실행 가능 . 대표적인 예시는 다음과 같음. 언어 사용할 수 있는 Kernel 예시 Python ipykernel R IRkernel C++ xeus-cling JavaScript ijavascript Java IJava 왜 이 개념이 필요할까? .ipynb = Python 파일 이라고 생각하면 Kernel의 역할을 이해하기 어려워지기 때문임. 정확하게는 다음 구조에 가까움. .ipynb ↓ 선택한 Kernel ↓ 해당 언어 실행 따라서 .ipynb 는 특정 언어 그 자체가 아니라 코드와 실행 결과 등을 담을 수 있는 Notebook 문서 형식 으로 이해하는 것이 중요함. 2. 그렇다면 Kernel은 무엇일까? ❓ 질문 Notebook 화면이 있는데 왜 Kernel이라는 것이 또 필요할까? VS Code나 Jupyter Notebook은 우리가 코드를 작성하고 결과를 확인하는 화면 을 제공함. 하지만 화면 자체가 Python 코드를 실행하는 것은 아님. 실제 코드를 실행해 주는 별도의 실행 엔진이 필요함. 이것이 Kernel 임. Kernel 코드를 전달받아 실제로 실행하고 결과를 다시 Notebook에 전달하는 실행 프로세스 쉽게 구조화하면 다음과 같음. 사용자 ↓ VS Code / Jupyter Notebook ↓ Kernel ↓ Python 실행 ↓ 실행 결과 ↓ VS Code / Jupyter Notebook 🍳 주방으로 비유하기 전체 구조를 식당으로 생각하면 이해하기 쉬움. 개념 비유 사용자 손님 VS Code / Jupyter 주문을 전달하는 공간 .ipynb 메뉴와 주문 내용이 적힌 종이 Kernel 실제 요리를 수행하는 주방장 가상환경 주방장이 사용하는 전용 주방 라이브러리 주방 안에 준비된 식재료와 조리도구 손님이 메뉴판에 주문을 적었다고 해서 음식이 자동으로 만들어지는 것은 아님. 실제로 요리하는 주방장 , 즉 Kernel이 필요한 구조임. 3. ipykernel은 무엇일까? ❓ 질문 그러면 ipykernel 은 그냥 Python을 실행하는 Kernel인 걸까? 핵심적으로 맞는 이해임. ipykernel 이라는 이름을 나누어 보면 역할을 이해하기 쉬움. i + py + kernel i → Interactive, 대화형 py → Python kernel → 코드를 실행하는 Kernel 즉, Python 코드를 대화형으로 실행할 수 있도록 Jupyter 환경과 연결해 주는 Python Kernel 이라고 이해 가능함. ipykernel은 IPython 기반의 Jupyter용 Python Kernel임. 4. 그런데 왜 '대화형(Interactive)'이라고 부를까? ❓ 질문 Python을 실행하는 건 알겠는데 왜 굳이 '대화형'이라는 이름을 사용할까? 핵심은 코드를 한 번에 전부 실행하는 것이 아니라 조금씩 실행하고 바로 결과를 확인할 수 있기 때문 임. 예를 들어 다음 코드를 실행한다고 가정함. a = 10 Kernel이 살아 있는 상태에서 다음 셀을 실행함. b = 20 그리고 다시 다음 코드를 실행함. a + b 결과: 30 첫 번째 셀에서 만든 a 와 두 번째 셀에서 만든 b 가 Kernel의 메모리에 남아 있기 때문에 세 번째 셀에서 다시 사용 가능함. 카카오톡으로 비유하기 사람과 다음과 같은 대화를 했다고 가정함. 나: 어제 치킨 먹었어. 상대방: 오. 나: 그거 맛있더라. 상대방은 앞의 대화를 기억하기 때문에 그거 = 치킨 이라는 사실을 이해함. 대화형 실행도 비슷한 구조임. a = 10 라고 먼저 실행하면 Kernel이 a 를 기억함. 이후 print(a) 만 실행해도 이전 실행 상태를 기억하고 있기 때문에 다음 결과 출력이 가능함. 10 대화형이라는 이름이 필요한 이유 데이터 분석과 AI 실험에서는 다음과 같은 확인 작업의 반복이 많기 때문임. 데이터 불러오기 ↓ 잘 불러왔는지 확인 ↓ 전처리 ↓ 결과 확인 ↓ 그래프 생성 ↓ 결과 확인 ↓ 모델 학습 ↓ 성능 확인 즉, 코드 작성 → 실행 → 결과 확인 → 수정 → 다시 실행 이라는 반복적인 작업 구조에 적합한 방식임. 5. Python Interpreter는 무엇일까? ❓ 질문 그런데 Kernel 말고 Python Interpreter라는 것도 나오는데 이건 무엇일까? Python Interpreter는 Python 코드를 읽고 실행하는 Python 실행기 임. 컴퓨터에 Python을 설치하면 사용할 수 있게 되는 python 실행 프로그램이 대표적인 Python Interpreter임. 예를 들어 터미널에서 다음 명령을 실행하는 상황임. python main.py 이때 main.py 를 읽고 실행하는 주체가 Python Interpreter임. Interpreter가 하는 일 Python 코드를 위에서부터 실행하면서 필요한 연산을 수행하고 오류가 발생하면 해당 위치에서 오류를 알려주는 역할 수행. 예를 들어 다음 코드가 있다고 가정함. a = 10 b = 20 print(a + b) Python Interpreter가 이 코드를 읽고 실행하여 다음 결과 생성. 30 6. Python Interpreter와 ipykernel은 무엇이 다를까? ❓ 질문 둘 다 Python 코드를 실행한다면 Python Interpreter와 ipykernel은 같은 것 아닐까? 완전히 같은 개념은 아님. Python Interpreter가 Python 코드 실행의 기본 주체 라면, ipykernel은 이 Python 실행 환경을 Jupyter와 연결하여 대화형으로 사용할 수 있도록 하는 Kernel 에 가까움. 구조를 단순화하면 다음과 같음. .py 실행 .py ↓ Python Interpreter ↓ 실행 Notebook에서는 다음 구조로 이해 가능함. .ipynb ↓ Jupyter / VS Code ↓ ipykernel ↓ Python 실행 환경 ↓ 실행 결과 반환 즉, ipykernel이 Python 자체를 대체하는 것이 아니라 Python 실행 환경을 Jupyter의 대화형 구조와 연결하는 역할 이라는 이해가 중요함. 7. 그렇다면 .ipynb 는 왜 Kernel을 선택해야 할까? ❓ 질문 .ipynb 파일은 Kernel을 연결해야 실행할 수 있는 것일까? 맞음. Notebook은 코드가 적혀 있는 문서이므로 어떤 실행 환경으로 해당 코드를 실행할 것인지 결정해야 함 . Python Notebook이라면 보통 Python 환경을 사용하는 ipykernel 선택이 필요함. VS Code에서는 오른쪽 위의 다음 메뉴에서 확인 가능함. Select Kernel 여기에서 프로젝트의 Python 가상환경 등을 선택하여 Notebook 실행 환경 지정 가능. 중요한 포인트 반드시 사용자가 매번 jupyter kernelspec 명령으로 직접 등록해야 한다는 의미는 아님. VS Code에서는 설치된 Python 환경을 찾아 Kernel로 선택할 수 있는 경우도 많음. 따라서 핵심은 다음과 같음. .ipynb 실행에는 어떤 Kernel을 사용할 것인지 연결이 필요함 8. 등록된 Kernel은 어떻게 확인할까? ❓ 질문 내 컴퓨터에는 어떤 Jupyter Kernel이 등록되어 있을까? 터미널에서 다음 명령 사용. jupyter kernelspec list 등록된 Kernel 목록 확인 가능. ❓ 질문 예전에 만든 Kernel이 너무 많다면 어떻게 삭제할까? 다음 명령 사용. jupyter kernelspec uninstall 삭제할커널이름 여기서 중요한 점은 Kernel 등록 정보를 삭제하는 것과 실제 Python 가상환경 폴더를 삭제하는 것은 다른 작업 이라는 점임. 즉, 일반적으로 kernelspec uninstall 은 Jupyter에 등록된 Kernel 정보를 제거하는 작업임. 9. 그렇다면 .py 파일은 무엇이 다를까? ❓ 질문 .ipynb 가 Kernel을 이용한다면 .py 는 완전히 다른 것일까? .py 는 일반적인 Python Script 파일 임. 예를 들어 다음 파일 존재. main.py 터미널에서 다음과 같이 실행 가능. python main.py Python Interpreter가 파일의 코드를 실행함. 10. .py 와 .ipynb 비교 구분 .py Python Script .ipynb Jupyter Notebook 기본 실행 방식 스크립트 단위 실행 Cell 단위 대화형 실행 실행 상태 실행 프로세스 종료 시 메모리 해제 Kernel이 살아 있는 동안 상태 유지 주요 용도 프로그램, 자동화, 서버, 배포 데이터 분석, AI 실험, 시각화 실행 환경 Python Interpreter Jupyter Kernel(ipykernel 등) 부분 실행 기본 실행은 전체 Script Cell 단위 실행 결과 확인 터미널 등 Notebook Cell 아래 Git 관리 일반 텍스트라 비교 용이 JSON 구조와 출력값 등으로 Diff가 복잡할 수 있음 11. 영화로 비유하면? .py 는 완성된 영화 에 가까움. 처음 ↓ 장면 1 ↓ 장면 2 ↓ 장면 3 ↓ 끝 일반적인 Script 실행에서는 처음부터 마지막까지 프로그램 흐름에 따라 실행됨. 반면 .ipynb 는 촬영 현장 과 비슷함. Scene 1 촬영 ↓ 결과 확인 Scene 2 촬영 ↓ 결과 확인 Scene 2 수정 ↓ 다시 촬영 따라서 데이터를 탐색하거나 AI 모델을 실험하는 과정에서 Notebook 방식의 장점 발생. 12. .py 가 끝나면 메모리는 어떻게 될까? ❓ 질문 .py 파일은 실행이 끝나면 메모리가 전부 사라지고 출력값만 남는 걸까? 일반적인 Script 실행 프로세스를 기준으로 보면 프로그램 종료와 함께 해당 프로세스가 사용하던 메모리도 해제됨. 흐름은 다음과 같음. 1 실행 시작 python main.py Python 프로세스 시작. 2 데이터 생성 a = 10 변수 a 가 실행 중인 프로세스의 메모리에 존재. 3 출력 print(a) 터미널에 다음 내용 출력. 10 4 프로그램 종료 마지막 코드까지 실행 완료 후 Python 프로세스 종료. 해당 프로세스가 사용하던 변수와 객체의 메모리 해제. 따라서 프로그램이 끝난 뒤 이전 실행의 a 를 다음 실행에서 그대로 가져오는 것은 불가능함. 13. 그렇다면 출력값은 남는 것 아닐까? 터미널에 다음 내용이 보일 수 있음. 10 하지만 이것은 a 라는 변수가 계속 Python 메모리에 존재한다는 의미가 아님. 터미널에 출력된 실행 기록 이 화면에 남아 있는 것임. 따라서 다음 실행에서 다시 해당 값이 필요하다면 재계산하거나 파일 등에 저장 필요. 예를 들어 CSV 저장. df.to_csv("result.csv") 이미지 저장. plt.savefig("result.png") 모델 저장. torch.save(model.state_dict(), "model.pt") 왜 저장이 필요할까? RAM의 변수와 SSD/HDD의 파일은 서로 다른 저장 방식이기 때문임. 프로그램 실행 중 ↓ RAM에 변수 존재 ↓ 프로그램 종료 ↓ RAM의 실행 상태 해제 반면 파일로 저장하면 다음 구조가 됨. 프로그램 실행 ↓ 결과 생성 ↓ 파일 저장 ↓ 프로그램 종료 ↓ SSD/HDD에 결과 파일 유지 14. 그런데 .py 에서도 pandas 같은 라이브러리가 필요하지 않을까? ❓ 질문 .py 는 Kernel이 필요 없다고 해도 pandas, numpy 같은 라이브러리는 필요하지 않을까? 당연히 필요함. 여기서 가장 중요한 구분이 등장함. Kernel 등록과 라이브러리 설치는 서로 다른 개념 15. 라이브러리 설치와 Kernel 등록의 차이 라이브러리 설치 pip install pandas 현재 Python 환경에 pandas 설치. Kernel 연결 Notebook이 어떤 Python 환경을 사용해서 코드를 실행할지 연결하는 과정 . 둘을 방으로 비유하면 다음과 같음. 가상환경 = 하나의 전용 방 pandas / numpy / torch = 방 안에 있는 도구 ipykernel = 그 방의 Python을 Jupyter와 연결해주는 역할 따라서 .py 와 .ipynb 모두 pandas를 사용한다면 실제로 실행되는 Python 환경에 pandas가 설치되어 있어야 함 . 16. 가상환경은 왜 필요할까? ❓ 질문 그냥 컴퓨터에 pandas를 한 번 설치하면 되는데 왜 굳이 가상환경을 만들까? 프로젝트마다 필요한 Python과 라이브러리 버전이 다를 수 있기 때문임. 예를 들어 다음 두 프로젝트 존재. 프로젝트 A Python 3.11 pandas 2.x torch 2.x 프로젝트 B Python 3.9 pandas 1.x tensorflow 특정 버전 모든 라이브러리를 하나의 전역 환경에 설치하면 프로젝트 간 버전 충돌 가능성 증가. 따라서 프로젝트별 독립 환경 구성. Project A └── .venv ├── Python ├── pandas └── torch Project B └── .venv ├── Python ├── pandas └── tensorflow 주방 비유 가상환경은 프로젝트 전용 주방 과 같은 개념. 프로젝트 A 주방에는 한식 재료가 있고, 프로젝트 B 주방에는 양식 재료가 있는 형태. 각 프로젝트가 자기에게 필요한 도구와 라이브러리만 사용하는 구조. 17. .py 는 어떤 가상환경의 라이브러리를 사용할까? ❓ 질문 컴퓨터에 가상환경이 여러 개 있다면 .py 는 어디의 pandas를 가져오는 걸까? 결국 어떤 Python Interpreter로 해당 파일을 실행했느냐 가 중요함. 가상환경을 활성화한 터미널에서 다음과 같이 실행한다고 가정함. python test.py 현재 활성화된 가상환경의 Python이 실행되고, 해당 환경에 설치된 라이브러리 사용. 예를 들어 macOS/Linux에서는 상황에 따라 다음과 같이 가상환경 활성화 가능. source .venv/bin/activate Windows에서는 환경과 Shell에 따라 다음과 같은 방식 사용. .venv\Scripts\activate 활성화 이후: python test.py 해당 가상환경의 Python과 라이브러리 사용. 18. VS Code의 Select Interpreter 는 무엇일까? ❓ 질문 VS Code에서 자꾸 Python: Select Interpreter 가 나오는데 도대체 무엇을 선택하라는 것일까? VS Code에게 다음을 알려주는 과정임. "이 프로젝트의 Python 코드를 어떤 Python 환경을 기준으로 처리할 것인가?" 컴퓨터에 다음 Python들이 존재할 수 있음. 시스템 Python Python 3.11 Python 3.12 Project A .venv Project B .venv Conda Environment VS Code 입장에서는 어떤 Python을 사용해야 하는지 결정 필요. 따라서 Select Interpreter 를 통해 프로젝트에서 사용할 Python 환경 선택. 19. VS Code에서 Python Interpreter 선택하기 macOS 명령 팔레트 실행. Cmd + Shift + P Windows Ctrl + Shift + P 검색창에 다음 입력. Select Interpreter 다음 메뉴 선택. Python: Select Interpreter 이후 프로젝트 내부 가상환경 선택. 예: .venv 또는 macOS/Linux의 경우 다음과 유사한 경로 확인. ./.venv/bin/python Windows에서는 다음과 유사한 경로 사용. .venv\Scripts\python.exe 20. 왜 Interpreter를 제대로 선택해야 할까? 예를 들어 .venv 에는 pandas가 설치되어 있다고 가정함. .venv ├── Python ├── pandas ├── numpy └── ipykernel 하지만 VS Code가 시스템 Python을 사용하고 있다면 다음 코드에서 오류 발생 가능. import pandas as pd 대표적인 오류: ModuleNotFoundError: No module named 'pandas' pandas가 컴퓨터에 전혀 없는 것이 아니라, 현재 코드를 실행하고 있는 Python 환경에는 pandas가 없는 상황 일 수 있음. 따라서 Python 환경 문제를 확인할 때 중요한 질문은 다음과 같음. "pandas를 설치했는가?" 뿐만 아니라 "지금 실행 중인 Python과 pandas를 설치한 Python이 같은 환경인가?" 라는 확인 필요. 21. 그렇다면 .venv 를 터미널에서 활성화하면 Interpreter 선택은 안 해도 될까? ❓ 질문 터미널에 (.venv) 가 떠 있다면 VS Code에서 Python Interpreter를 따로 선택하지 않아도 될까? 실행 방식에 따라 구분 필요. Case 1. 활성화된 터미널에서 직접 실행 터미널에 다음처럼 가상환경이 활성화되어 있다고 가정함. (.venv) 그리고 직접 다음 명령 실행. python test.py 이 경우 Shell의 python 이 활성화된 .venv 의 Python을 가리키도록 설정되어 있으므로 해당 환경을 이용한 실행 가능. 활성화된 .venv ↓ python test.py ↓ .venv의 Python ↓ .venv의 pandas 사용 Case 2. VS Code의 실행 버튼 이용 VS Code에서 오른쪽 위의 ▶ 버튼 등을 사용하는 경우에는 VS Code가 선택하고 있는 Python 환경 설정도 중요함. 따라서 VS Code 작업에서는 프로젝트의 .venv 를 Interpreter로 선택해 두는 것이 안전함. Cmd/Ctrl + Shift + P ↓ Python: Select Interpreter ↓ 프로젝트 .venv 선택 추천 설정 프로젝트를 열었을 때 한 번 다음 설정 수행. Select Interpreter ↓ .venv 선택 이를 통해 코드 분석, 실행, 디버깅 등의 환경을 프로젝트 가상환경과 일치시키기 쉬워짐. 22. 그런데 .py 에서도 Notebook처럼 조금씩 실행할 수 없을까? ❓ 질문 .py 파일은 항상 처음부터 끝까지 실행해야 할까? 일반적인 Script 실행은 파일 단위 실행이지만 VS Code에서는 .py 파일을 Cell 형태로 나누어 Interactive Window에서 실행하는 기능 사용 가능. 이때 사용하는 것이 다음 주석임. # %% 23. # %% 란 무엇일까? .py 파일에 다음처럼 작성. # %% [1번 블록] 데이터 정의하기 import pandas as pd a = 10 b = 20 print("데이터 입력 완료!") 다음 Cell 작성. # %% [2번 블록] 계산하기 result = a + b print(f"결과는? {result}") VS Code가 # %% 를 기준으로 코드 영역을 Cell처럼 인식 가능. 화면 위에 다음과 같은 메뉴 표시 가능. Run Cell Run Below Debug Cell Run Cell 을 누르면 해당 영역만 실행 가능. 24. # %% 을 사용하면 왜 메모리가 유지될까? ❓ 질문 .py 인데 왜 첫 번째 블록에서 만든 a 를 두 번째 블록에서 사용할 수 있을까? python test.py 로 일반 Script 실행을 하는 것이 아니라 VS Code의 Interactive Window 를 통해 실행하고 있기 때문임
velog
PWM 이란 Pulse Width Modulation 정보나 제어값에 따라 Pulse 폭을 변화시켜 전달하는 방식 Pulse 폭은 한 주기에서 신호가 HIGH로 유지되는 시간을 의미함 한 주기에서 HIGH 시간이 차지하는 비율 을 듀티비(Duty Cycle) 라고 함 듀티비를 조절하여 부하에 공급되는 평균 파워 변화 가능 주로 LED 밝기 제어, DC 모터 속도 제어 등에 사용함 듀티비(%) = HIGH로 유지되는 시간 / 전체 주기 × 100 ex) 전체 주기가 4ms이고 HIGH 시간이 1ms라면 듀티비는 25% PWM 생성 과정 PWM 생성 시 사용하는 Register 4가지 레지스터 역할 PSC — Prescaler 카운터가 숫자를 세는 속도 결정 CNT — Counter 현재 세고 있는 숫자 ARR — Auto-Reload Register 카운터의 최대치 설정 CCR — Capture/Compare Register 출력이 HIGH에서 LOW로 바뀌는 기준값 CNT가 CCR 보다 낮을 경우 HIGH 그 외 LOW _CNT와 CCR 비교는 TIM이 수행 _
Балл: 54.39Уверенность: 49%
ПодробнееБалл: 54.4Уверенность: 49%
Балл: 54.4Уверенность: 49%
Балл: 54.4Уверенность: 49%