오차를 더하면 0인데 왜 틀릴까 — AI로 만드는 최소제곱 조작 실험

자료ID AA-20261020-INTL-RESIDUAL · 자체 제작 예제 · 실제 수업 적용 전 교사 검토
Dan McCreary의 MicroSims 저장소는 AI로 만든 교육용 상호작용 자료를 공개합니다. Least Squares 페이지에는 기울기·절편 조절, 실행·편집 링크와 버전 이력이 있습니다. 아래 실습은 별도의 작은 데이터와 자체 코드를 사용해 오차의 상쇄를 살펴봅니다. 원본 코드를 번역하거나 복제한 재현물이 아닙니다.
학습목표
학습자는 잔차합이 0이어도 모든 예측이 맞는 것은 아님을 설명하고, 제곱합으로 두 직선을 비교합니다. 교사는 그래프와 수치표가 같은 계산을 사용하는 조작 자료를 만듭니다.
준비물·계정·비용
제공한 lab.html과 브라우저만 있으면 계정·API 없이 실행됩니다. 코드 생성 도구를 추가로 이용할 때 요금·사용량은 선택한 계정 기준으로 확인합니다. 원사이트 자료는 CC BY-NC-SA 4.0 표시가 있으므로 상업 도서에 원그림·코드를 전재하는 허가와 혼동하지 않습니다.
예제입력
자체 설명용 관측값: (1,2), (2,2), (3,4). 실제 실험 데이터가 아니다. 모형 A: y=x → 잔차 1,0,1 / 잔차합 2 / 제곱합 2. 모형 B: y=8/3 → 잔차 -2/3,-2/3,4/3 / 잔차합 0 / 제곱합 8/3. 최소제곱: y=x+2/3 → 잔차 1/3,-2/3,1/3 / 제곱합 2/3.
번호별 제작 절차
- 교사 기준표의 세 좌표를 먼저 고정하고 실제 측정 데이터가 아니라는 표시를 넣습니다.
- 최초 프롬프트로 기울기 a와 절편 b를 조작하는 HTML을 만듭니다.
- a=1,b=0으로 두고 예측값·잔차·제곱을 표에서 손계산과 대조합니다.
- a=0,b=8/3인 비교 버튼을 눌러 잔차합 0과 제곱합 8/3이 함께 나오는지 봅니다.
- 최소제곱 버튼의 a=1,b=2/3과 비교하고 어떤 수치가 더 작은지 기록합니다. 화면 반올림값을 다시 계산에 넣지 않습니다.
- 학생은 예측선이 점 사이를 통과해도 잔차가 남는 이유를 쓰고, x=10의 예측을 실제 관측처럼 말할 수 없는 이유를 토론합니다.
복사해 쓰는 자체 한국어 응용 프롬프트
외부 원본 프롬프트의 전문 번역이 아닙니다. 아래 지시는 이 글의 예제를 위해 새로 작성했습니다. 최초 지시에 위 예제입력을 함께 붙여 넣으세요.
최초
좌표 (1,2),(2,2),(3,4)를 쓰는 단일 HTML 실습을 만들어라. a,b 숫자 입력으로 y=ax+b를 바꾸며 산점도·직선·세로 잔차선을 그려라. 표에는 x,실제y,예측y,잔차,잔차제곱을 표시하라. 잔차합과 제곱합을 함께 표시하고 모형 A(1,0), 모형 B(0,8/3), 최소제곱(1,2/3) 버튼을 넣어라. 내부 계산은 분수에 해당하는 정밀도를 유지하고 출력만 반올림하라. 외부 라이브러리와 API를 쓰지 마라. 설명용 가상 데이터임을 표시하라.
점검
a=1,b=0에서 SSE=2, a=0,b=8/3에서 SSE=8/3, a=1,b=2/3에서 SSE=2/3인지 독립 계산하라. 잔차합이 0이라는 이유로 완벽히 맞는다고 판단하는 문구를 찾아라. 빈 입력·숫자 아닌 입력은 0으로 조용히 처리하지 말고 오류를 안내하라.
수정
그래프를 예쁘게 하려고 데이터 좌표를 이동시키지 마라. y축 방향이 화면 좌표와 반대인 부분만 고쳐라. 예측선과 잔차표가 같은 a,b를 사용하게 하고 모든 관측점이 보이도록 축 범위를 유지하라. 화면 표시의 0.667을 계산 원본으로 쓰지 마라.
확장
네 번째 점 (4,4)을 추가했을 때 최소제곱 계수를 다시 구하는 확장안을 설계하라. 기존 3점 모드와 4점 모드를 구분하고 모드에 맞는 기준표를 갱신하라. 아직 코드를 수정하지 않았다면 확장 실행 완료라고 표시하지 마라.
기대결과
모형 B는 잔차합이 0이지만 SSE가 2.666667로 남습니다. 최적 모형의 SSE는 0.666667이며 어느 점도 모두 정확히 맞추지는 않습니다.
실제결과와 검증 범위
자체 HTML을 내부 브라우저에서 실행했습니다. 모형 A의 SSE 2, 모형 B의 2.666667, 최소제곱의 0.666667과 빈 입력 오류 안내를 확인했습니다. Python Fraction 독립 검산도 통과했습니다. 원본 MicroSim 조작·학생 수업은 미실시입니다.
오류해결
잔차합이 0이면 100% 정답이라는 설명은 삭제합니다. SSE가 음수라면 제곱 대신 잔차를 더했는지 확인합니다. 절편 입력을 소수 세 자리로 고정하면 기준값과 약간 달라질 수 있으므로 분수 기준 버튼으로 재비교합니다.
연습·평가기준
두 직선 중 잔차합이 작은 쪽이 반드시 SSE도 작은지 위 예제로 반박하세요. 평가 10점: 세 모형 계산 4점, 상쇄 설명 3점, 반올림·가상 데이터 구분 2점, 외삽 한계 1점.
기존 글과의 차이
기존 진자·열전도·유체 실습과 달리 모형 적합과 오차 상쇄를 다룹니다.
공식·원저자 출처
Least Squares / Micro Simulations for Education — Dan McCreary. 게시일: 미확인. 확인: 2026-10-20. 제작자 페이지의 실행 링크·편집 링크·수치·버전 이력 확인. 저장소가 AI 생성 교육자료임을 명시.
원본 프롬프트: 버전2 JS 머리 주석에 있다는 위치 확인; 해당 주석 원문은 아직 미열람. 이용조건: CC BY-NC-SA 4.0; 상업 도서 전재 허가로 간주하지 않음.
MicroSims 원저자 저장소. 원문에 소개된 학습효과를 이번 예제의 실측 결과로 사용하지 않았습니다.
자체 제작 전체 실행 코드
아래 접힌 영역을 열어 코드 전체를 복사하고, UTF-8 텍스트 파일 lab.html로 저장한 뒤 브라우저로 여세요. 별도 계정이나 API가 필요 없습니다.
전체 HTML 코드 펼치기
<!doctype html><html lang="ko"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>잔차 실험</title><style>body{font:18px/1.7 system-ui;max-width:850px;margin:30px auto;padding:20px;color:#123}input,button{font:inherit;padding:8px;margin:6px}input{width:130px}svg{width:100%;background:#f1f6fa}table{border-collapse:collapse;width:100%}td,th{padding:8px;border-bottom:1px solid #bac}#sum{font-weight:bold}</style><h1>잔차합이 0이면 완벽할까요?</h1><p>설명용 가상 데이터 (1,2), (2,2), (3,4) · 외부 API 없음</p><label>기울기 a <input id="a" type="number" step="any" value="1"></label><label>절편 b <input id="b" type="number" step="any" value="0"></label><div><button id="modelA">모형 A</button><button id="modelB">모형 B</button><button id="best">최소제곱</button></div><p id="error" role="alert"></p><svg id="plot" viewBox="0 0 600 350" role="img" aria-label="관측점과 예측선, 세로 잔차선. 수치는 아래 표 제공"></svg><p id="sum" role="status"></p><table><thead><tr><th>x</th><th>실제 y</th><th>예측 y</th><th>잔차</th><th>제곱</th></tr></thead><tbody id="rows"></tbody></table><p>잔차합은 서로 상쇄됩니다. 제곱합이 남는다면 예측 오차가 남아 있습니다.</p><script>
const data=[[1,2],[2,2],[3,4]],el=id=>document.getElementById(id),fmt=n=>(Math.abs(n)<1e-10?0:n).toFixed(6);function render(){const a=el('a').valueAsNumber,b=el('b').valueAsNumber;if(!Number.isFinite(a)||!Number.isFinite(b)){el('error').textContent='a와 b에 유효한 숫자를 입력하세요.';el('sum').textContent='계산 대기';el('rows').innerHTML='';el('plot').innerHTML='';return}el('error').textContent='';let sum=0,sse=0;el('rows').innerHTML=data.map(([x,y])=>{const p=a*x+b,r=y-p;sum+=r;sse+=r*r;return '<tr>'+[x,y,fmt(p),fmt(r),fmt(r*r)].map(v=>'<td>'+v+'</td>').join('')+'</tr>'}).join('');el('sum').textContent=`잔차합 ${fmt(sum)} / 제곱합 SSE ${fmt(sse)}`;const ys=[0,5,b,4*a+b,...data.map(([x])=>a*x+b)],lo=Math.min(...ys),hi=Math.max(...ys),X=x=>50+125*x,Y=y=>300-250*(y-lo)/(hi-lo);let s='<path d="M50 25V300H565" fill="none" stroke="#567"/><text x="570" y="315">x</text><text x="30" y="22">y</text>';for(let x=0;x<=4;x++)s+=`<text x="${X(x)}" y="325">${x}</text>`;for(let j=0;j<=5;j++){let y=lo+(hi-lo)*j/5;s+=`<text x="0" y="${Y(y)}">${y.toFixed(1)}</text>`}s+=`<line x1="${X(0)}" y1="${Y(b)}" x2="${X(4)}" y2="${Y(4*a+b)}" stroke="#168" stroke-width="3"/>`;data.forEach(([x,y])=>{s+=`<line x1="${X(x)}" y1="${Y(y)}" x2="${X(x)}" y2="${Y(a*x+b)}" stroke="#b42" stroke-width="3" stroke-dasharray="5 4"/><circle cx="${X(x)}" cy="${Y(y)}" r="6" fill="#17283b"/>`});el('plot').innerHTML=s}function set(a,b){el('a').value=a;el('b').value=b;render()}el('modelA').onclick=()=>set(1,0);el('modelB').onclick=()=>set(0,8/3);el('best').onclick=()=>set(1,2/3);el('a').oninput=render;el('b').oninput=render;render();
</script></html>
댓글
댓글 쓰기