-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathioai_mapping.html
More file actions
280 lines (271 loc) · 19.4 KB
/
Copy pathioai_mapping.html
File metadata and controls
280 lines (271 loc) · 19.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>IOAI 2024·2025 ↔ 기본기 노트북 매핑</title>
<style>
:root{
--bg:#0f1320; --panel:#171c2e; --panel2:#1e2540; --line:#2c3556;
--tx:#e7ebf5; --mut:#9aa6c7;
--cat-tabular:#4f8cff; --cat-nlp:#ff8a5c; --cat-vision:#3ecf8e;
--cat-multi:#c08bff; --cat-gen:#ff6b9d; --cat-audio:#ffd166; --cat-tech:#8fa3bf;
--d1:#3ecf8e; --d2:#4f8cff; --d3:#ffab4a; --d4:#ff5d6c;
}
*{box-sizing:border-box}
html,body{height:100%}
body{margin:0;height:100vh;overflow:hidden;display:flex;flex-direction:column;
background:linear-gradient(180deg,#0b0e18,#0f1320);color:var(--tx);
font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Apple SD Gothic Neo","Malgun Gothic",sans-serif;}
header{padding:14px 26px 8px;flex:0 0 auto;display:flex;align-items:center;gap:22px;flex-wrap:wrap}
h1{margin:0;font-size:21px;letter-spacing:-.2px;white-space:nowrap}
.legend{display:flex;flex-wrap:wrap;gap:14px;font-size:13px;color:var(--mut);align-items:center}
.chip{display:inline-flex;align-items:center;gap:5px}
.dot{width:12px;height:12px;border-radius:3px;display:inline-block}
.d{width:22px;height:9px;border-radius:4px;display:inline-block}
.hint{font-size:12.5px;color:var(--mut);padding:0 26px 6px;flex:0 0 auto}
#board{position:relative;flex:1 1 auto;display:grid;grid-template-columns:minmax(320px,1fr) minmax(520px,1.3fr);
gap:80px;padding:6px 26px 20px;overflow:hidden;min-height:0}
svg#links{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;z-index:1}
.col{min-height:0;display:flex;flex-direction:column}
.col>h2{font-size:13px;color:var(--mut);text-transform:uppercase;letter-spacing:1px;margin:0 0 8px;flex:0 0 auto}
.pane{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;gap:7px}
.yhdr{font-size:13px;color:var(--mut);font-weight:700;margin:2px 0 0;flex:0 0 auto}
#colP .node{flex:1 1 0}
.grid2{display:grid;grid-template-columns:1fr 1fr;grid-auto-rows:1fr;gap:9px 16px;flex:1 1 auto;min-height:0}
.node{position:relative;z-index:2;background:var(--panel);border:1px solid var(--line);border-left-width:5px;
border-radius:9px;padding:8px 15px;cursor:pointer;transition:.12s;
display:flex;align-items:center;gap:9px;font-size:15px;line-height:1.2}
.node .num{font-size:13px;color:var(--mut);font-variant-numeric:tabular-nums}
.node .name{font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.node .dd{width:12px;height:12px;border-radius:50%;margin-left:auto;flex:0 0 auto}
.node.dim{opacity:.18;filter:saturate(.3)}
.node.hot{box-shadow:0 0 0 2px #fff4,0 4px 16px #0009;z-index:3}
.node.on{outline:2px solid #ffffff55}
/* 노트북 반복횟수 카운터 — 난이도 동그라미 왼쪽, 클릭 +1 */
#colN .node .dd{margin-left:0}
.rep{margin-left:auto;flex:0 0 auto;display:inline-flex;align-items:center;gap:3px;
font-size:12px;color:var(--mut);background:#0006;border:1px solid var(--line);
border-radius:20px;padding:1px 9px;cursor:pointer;user-select:none;
font-variant-numeric:tabular-nums}
.rep:hover{background:#28315a;color:var(--tx)}
.rep.has{color:#ffd166;border-color:#ffd16655;background:#ffd1660f}
/* 완주 보상 토스트 — 화면 정중앙 (클릭하면 닫힘) */
#nbtoast{position:fixed;left:50%;top:50%;transform:translate(-50%,-50%) scale(.9);
max-width:min(78vw,640px);background:#171c2e;border:1px solid #2c3556;border-top:5px solid #3ecf8e;
color:#e7ebf5;padding:24px 34px 30px;border-radius:16px;font-size:19px;line-height:1.7;text-align:center;
box-shadow:0 20px 70px #000c;opacity:0;pointer-events:none;cursor:pointer;transition:.18s;z-index:90}
#nbtoast.show{opacity:1;pointer-events:auto;transform:translate(-50%,-50%) scale(1)}
#nbtoast.err{border-top-color:#ff5d6c;font-size:16px;padding:18px 26px 24px}
#nbtoast b{color:#ffd166;font-size:1.08em}
#nbtoast .nbclose{position:absolute;top:6px;right:12px;font-size:14px;color:#9aa6c7;line-height:1}
#nbtoast .nbhint{margin-top:10px;font-size:11px;color:#9aa6c7;opacity:.8}
</style>
</head>
<body>
<header>
<h1>🧭 IOAI 2024·2025 ↔ 기본기 노트북</h1>
<div class="legend">
<span class="chip"><span class="dot" style="background:var(--cat-tabular)"></span>테이블러</span>
<span class="chip"><span class="dot" style="background:var(--cat-nlp)"></span>NLP</span>
<span class="chip"><span class="dot" style="background:var(--cat-vision)"></span>비전</span>
<span class="chip"><span class="dot" style="background:var(--cat-multi)"></span>멀티모달</span>
<span class="chip"><span class="dot" style="background:var(--cat-gen)"></span>생성</span>
<span class="chip"><span class="dot" style="background:var(--cat-audio)"></span>오디오</span>
<span class="chip"><span class="dot" style="background:var(--cat-tech)"></span>기법</span>
|
<span class="chip"><span class="d" style="background:var(--d1)"></span>입문</span>
<span class="chip"><span class="d" style="background:var(--d2)"></span>기초</span>
<span class="chip"><span class="d" style="background:var(--d3)"></span>중급</span>
<span class="chip"><span class="d" style="background:var(--d4)"></span>고급</span>
</div>
</header>
<div class="hint">💡 문제·노트북에 마우스를 올리면 연결선이 그려집니다 · 노드 위에 커서를 두면 상세 설명(툴팁) 표시</div>
<div id="board">
<svg id="links"></svg>
<div class="col"><h2>◀ IOAI 2024·2025 문제 (13)</h2><div class="pane" id="colP"></div></div>
<div class="col"><h2>우리 기본기 노트북 (26) ▶</h2><div class="pane" id="colN"></div></div>
</div>
<script>
const CAT={tabular:{c:'#4f8cff',l:'테이블러'},nlp:{c:'#ff8a5c',l:'NLP'},vision:{c:'#3ecf8e',l:'비전'},
multi:{c:'#c08bff',l:'멀티모달'},gen:{c:'#ff6b9d',l:'생성'},audio:{c:'#ffd166',l:'오디오'},tech:{c:'#8fa3bf',l:'기법'}};
const DIFF={1:{l:'입문',c:'#3ecf8e'},2:{l:'기초',c:'#4f8cff'},3:{l:'중급',c:'#ffab4a'},4:{l:'고급',c:'#ff5d6c'}};
const NB=[
{id:'n01',no:'01',name:'RandomForest',cat:'tabular',diff:1,src:'titanic'},
{id:'n02',no:'02',name:'LightGBM',cat:'tabular',diff:1,src:'titanic'},
{id:'n03',no:'03',name:'XGBoost',cat:'tabular',diff:2,src:'house-prices'},
{id:'n04',no:'04',name:'CatBoost',cat:'tabular',diff:2,src:'house-prices'},
{id:'n05',no:'05',name:'Ensemble',cat:'tabular',diff:3,src:'house-prices'},
{id:'n06',no:'06',name:'K-Fold(OOF)',cat:'tabular',diff:2,src:'house-prices'},
{id:'n07',no:'07',name:'Ridge+TF-IDF',cat:'nlp',diff:1,src:'nlp-getting-started'},
{id:'n08',no:'08',name:'CNN',cat:'vision',diff:2,src:'digit-recognizer'},
{id:'n09',no:'09',name:'U-Net(분할)',cat:'vision',diff:3,src:'data-science-bowl-2018'},
{id:'n10',no:'10',name:'ResNet(전이학습)',cat:'vision',diff:2,src:'dogs-vs-cats'},
{id:'n11',no:'11',name:'CLIP(제로샷)',cat:'multi',diff:3,src:'dogs-vs-cats'},
{id:'n12',no:'12',name:'BERT(미세조정)',cat:'nlp',diff:3,src:'nlp-getting-started'},
{id:'n13',no:'13',name:'동결특징+KNN(증분)',cat:'tech',diff:3,src:'digit-recognizer'},
{id:'n14',no:'14',name:'피처엔지니어링+선형회귀',cat:'tabular',diff:2,src:'house-prices'},
{id:'n15',no:'15',name:'조건부 VAE 생성-검증',cat:'gen',diff:4,src:'digit-recognizer'},
{id:'n16',no:'16',name:'대조학습 임베딩(MNR)',cat:'nlp',diff:4,src:'nlp-getting-started'},
{id:'n17',no:'17',name:'U-Net 가중CE 분할',cat:'vision',diff:3,src:'data-science-bowl-2018'},
{id:'n18',no:'18',name:'메타데이터 FiLM+주기인코딩',cat:'tech',diff:3,src:'digit-recognizer'},
{id:'n19',no:'19',name:'오디오 스펙트로그램 CNN',cat:'audio',diff:3,src:'dont-call-me-turkey'},
{id:'n20',no:'20',name:'문자 BiLSTM 분절',cat:'nlp',diff:3,src:'nlp-getting-started'},
{id:'n21',no:'21',name:'BiLSTM NER',cat:'nlp',diff:3,src:'mipt-ner'},
{id:'n22',no:'22',name:'CLIP 박스 탐색(coarse-to-fine)',cat:'multi',diff:4,src:'dogs-vs-cats'},
{id:'n23',no:'23',name:'준지도학습',cat:'tech',diff:3,src:'digit-recognizer'},
{id:'n24',no:'24',name:'밀도 추정 카운팅',cat:'vision',diff:4,src:'data-science-bowl-2018'},
{id:'n25',no:'25',name:'Radar 가중CE U-Net',cat:'vision',diff:3,src:'radar-ioai-2025'},
{id:'n26',no:'26',name:'Restroom CLIP 매칭',cat:'multi',diff:3,src:'restroom-ioai-2025'},
];
const PB=[
{id:'p_help',t:'Help BOBAI',y:2024,r:'On-Site',cat:'nlp',diff:3,tech:'KNeighbors+StandardScaler (frozen 임베딩 위 kNN, 클래스 확장)',maps:['n13','n12','n16']},
{id:'p_hyper',t:'Lost in Hyperspace',y:2024,r:'On-Site',cat:'tabular',diff:3,tech:'PCA+LinearRegression (차원축소+회귀, 피처엔지니어링)',maps:['n14','n03','n06']},
{id:'p_cow',t:'Madarian Cow',y:2024,r:'On-Site',cat:'gen',diff:4,tech:'생성+검증 (frozen 생성기 + 분류기 검증)',maps:['n15','n08']},
{id:'p_cham',t:'Chameleon',y:2025,r:'At-Home',cat:'multi',diff:4,tech:'문장 임베딩 대조학습(MNR) + 코사인 유사도',maps:['n16','n11','n13']},
{id:'p_radar',t:'Radar',y:2025,r:'At-Home/Indiv',cat:'vision',diff:3,tech:'가중 CrossEntropy U-Net (비-RGB 신호 텐서 분할)',maps:['n25','n17','n09']},
{id:'p_wthr',t:'Weather (위성 강수)',y:2025,r:'At-Home',cat:'vision',diff:4,tech:'메타데이터 FiLM + 주기 인코딩 (이미지→연속값)',maps:['n18','n17']},
{id:'p_anti',t:'Antique 인증',y:2025,r:'Individual',cat:'vision',diff:3,tech:'준지도학습 (라벨 없는 데이터 활용) + frozen 특징',maps:['n23','n13','n17']},
{id:'p_chk',t:'Chicken Counting',y:2025,r:'Individual',cat:'vision',diff:4,tech:'밀도 추정 카운팅 (density map)',maps:['n24','n17','n09']},
{id:'p_conc',t:'Concepts',y:2025,r:'Individual',cat:'multi',diff:4,tech:'대조학습 임베딩 + 멀티모달(CLIP)',maps:['n16','n11']},
{id:'p_pix',t:'Pixel Efficiency',y:2025,r:'Individual',cat:'tech',diff:4,tech:'CLIP coarse-to-fine 박스 탐색 (중요 영역 식별)',maps:['n22','n11','n13']},
{id:'p_rest',t:'Restroom 매칭',y:2025,r:'Individual',cat:'multi',diff:3,tech:'2단계 CLIP 미세조정 매칭(Re-ID)',maps:['n26','n16','n11']},
{id:'p_spch',t:'Synthetic Speech',y:2025,r:'GAITE',cat:'audio',diff:3,tech:'스펙트로그램 CNN (오디오 분류)',maps:['n19','n10']},
{id:'p_word',t:'Word Segmentation',y:2025,r:'GAITE',cat:'nlp',diff:3,tech:'문자 BiLSTM 시퀀스 라벨링/분절',maps:['n20','n21']},
];
const REV={};NB.forEach(n=>REV[n.id]=[]);PB.forEach(p=>p.maps.forEach(id=>{if(REV[id])REV[id].push(p.id)}));
const badge=c=>`<span class="badge" style="background:${CAT[c].c}22;color:${CAT[c].c};border:1px solid ${CAT[c].c}66">${CAT[c].l}</span>`;
const diffB=d=>`<span class="diff" style="background:${DIFF[d].c}">${DIFF[d].l}</span>`;
// 문제 컬럼 (연도 그룹)
const colP=document.getElementById('colP');
[[2024,'IOAI 2024 · On-Site'],[2025,'IOAI 2025 · At-Home/Individual/GAITE']].forEach(([yr,lbl])=>{
const h=document.createElement('div');h.className='yhdr';h.textContent=lbl;colP.appendChild(h);
PB.filter(p=>p.y===yr).forEach(p=>{
const el=document.createElement('div');el.className='node';el.id='node_'+p.id;
el.style.borderLeftColor=CAT[p.cat].c;
el.title=`[${DIFF[p.diff].l}·${CAT[p.cat].l}] ${p.r}\n${p.tech}`;
el.innerHTML=`<span class="name">${p.t}</span><span class="dd" style="background:${DIFF[p.diff].c}"></span>`;
el.dataset.kind='p';el.dataset.targets=p.maps.join(',');el.dataset.self=p.id;
colP.appendChild(el);
});
});
// 노트북 완주 횟수 — 서버 권위(/api/notebooks). REPS는 표시용 캐시.
const REPS={}, repEls={};
function paintRep(el,id){const c=REPS[id]||0;el.textContent='↻ '+c;el.classList.toggle('has',c>0);}
function nbToast(html,isErr){let t=document.getElementById('nbtoast');
if(!t){t=document.createElement('div');t.id='nbtoast';document.body.appendChild(t);
t.addEventListener('click',()=>{clearTimeout(t._h);t.classList.remove('show');});} // 클릭하면 즉시 닫힘
t.className=isErr?'err':'ok';
t.innerHTML='<span class="nbclose">✕</span>'+html+'<div class="nbhint">아무 곳이나 클릭하면 닫혀요</div>';
t.classList.add('show');
clearTimeout(t._h);t._h=setTimeout(()=>t.classList.remove('show'),isErr?2500:2500);} // 2.5초 후 자동 닫힘
async function loadCounts(){try{const r=await fetch('/api/notebooks',{credentials:'same-origin'});
if(!r.ok)return;const d=await r.json();Object.assign(REPS,d.counts||{});
for(const id in repEls)paintRep(repEls[id],id);}catch(e){}}
let nbBusy=false;
async function completeNotebook(n,rep){
if(nbBusy)return;
if(!confirm(`'${n.no} ${n.name}' 노트북을 1회 완주했나요?\n확인을 누르면 보상이 지급됩니다.`))return;
nbBusy=true;rep.style.opacity='0.5';
try{
const r=await fetch(`/api/notebooks/${n.id}/complete`,{method:'POST',credentials:'same-origin'});
const d=await r.json().catch(()=>({}));
if(!r.ok){nbToast('❌ '+(d.detail||'오류가 발생했습니다'),true);return;}
if(d.ok===false){nbToast(d.reason==='daily_limit'
?'⏳ 오늘은 이미 이 노트북 완주 보상을 받았어요 — 경험치는 하루 1회':'처리 실패',true);return;}
REPS[n.id]=d.count;paintRep(rep,n.id);
let m=`<div style="font-size:1.15em;color:#3ecf8e;font-weight:800;margin-bottom:8px">🎉 축하합니다. 코딩테스트 1문제를 풀으셨어요!!!</div>`;
m+=`✅ <b>${n.no} ${n.name}</b> 완주! <span style="color:#9aa6c7">(${d.count}회)</span><br>`;
m+=`+${(d.xp||0).toLocaleString()} XP`;
if(d.gold)m+=` · +${d.gold.toLocaleString()} G`;
const eg=(d.eggs||0);if(eg)m+=` · 🥚×${eg}`;
if(d.is_first)m+=`<br>🌟 첫 완주 보너스 ×2`;
if(d.event_double)m+=` · ⚡ 이벤트 ×2`;
if(d.multiplier&&d.multiplier>1)m+=` <span style="color:#9aa6c7">(버프 ×${d.multiplier})</span>`;
(d.milestones||[]).forEach(ms=>{m+=`<br>🏆 칭호 획득: <b>${ms.name}</b> (+${(ms.xp||0).toLocaleString()} XP)`;});
nbToast(m);
try{window.parent.dispatchEvent(new Event('koai:profile-refresh'));}catch(e){}
}catch(e){nbToast('❌ 네트워크 오류',true);}
finally{nbBusy=false;rep.style.opacity='';}
}
setTimeout(loadCounts,0);
// 노트북 컬럼 (2열 그리드, 색=분류)
const colN=document.getElementById('colN');
const grid=document.createElement('div');grid.className='grid2';colN.appendChild(grid);
NB.forEach(n=>{
const el=document.createElement('div');el.className='node';el.id='node_'+n.id;
el.style.borderLeftColor=CAT[n.cat].c;
el.title=`[${DIFF[n.diff].l}·${CAT[n.cat].l}] ${n.name}\n소스: ${n.src}`;
el.innerHTML=`<span class="num">${n.no}</span><span class="name">${n.name}</span>`+
`<span class="rep" title="1회 완주 기록 — 클릭 시 확인 후 보상 지급"></span>`+
`<span class="dd" style="background:${DIFF[n.diff].c}"></span>`;
el.dataset.kind='n';el.dataset.targets=(REV[n.id]||[]).join(',');el.dataset.self=n.id;
grid.appendChild(el);
// 완주 카운터: 클릭 → 확인 → 서버 보상 지급 (노드 클릭=Colab 열기와 분리)
const rep=el.querySelector('.rep');repEls[n.id]=rep;paintRep(rep,n.id);
rep.addEventListener('click',ev=>{ev.stopPropagation();completeNotebook(n,rep);});
});
// SVG 라인
const svg=document.getElementById('links'),board=document.getElementById('board'),NS='http://www.w3.org/2000/svg';
const clr=()=>{while(svg.firstChild)svg.removeChild(svg.firstChild)};
function draw(selfId,targets,kind){
clr();const br=board.getBoundingClientRect();
const a=document.getElementById('node_'+selfId).getBoundingClientRect();
const col=CAT[(kind==='p'?PB.find(p=>p.id===selfId).cat:NB.find(n=>n.id===selfId).cat)].c;
targets.forEach(tid=>{
const tn=document.getElementById('node_'+tid);if(!tn)return;const b=tn.getBoundingClientRect();
let x1,y1,x2,y2;
if(kind==='p'){x1=a.right-br.left;y1=a.top+a.height/2-br.top;x2=b.left-br.left;y2=b.top+b.height/2-br.top;}
else{x1=b.right-br.left;y1=b.top+b.height/2-br.top;x2=a.left-br.left;y2=a.top+a.height/2-br.top;}
const mx=(x1+x2)/2,p=document.createElementNS(NS,'path');
p.setAttribute('d',`M${x1},${y1} C${mx},${y1} ${mx},${y2} ${x2},${y2}`);
p.setAttribute('fill','none');p.setAttribute('stroke',col);p.setAttribute('stroke-width','2');p.setAttribute('opacity','.9');
svg.appendChild(p);
});
}
function hi(selfId,targets){const keep=new Set([selfId,...targets]);
document.querySelectorAll('.node').forEach(el=>{const id=el.dataset.self;
el.classList.toggle('dim',!keep.has(id));el.classList.toggle('hot',id===selfId);el.classList.toggle('on',keep.has(id)&&id!==selfId);});}
const reset=()=>{clr();document.querySelectorAll('.node').forEach(el=>el.classList.remove('dim','hot','on'))};
document.querySelectorAll('.node').forEach(el=>el.addEventListener('mouseenter',()=>{
const s=el.dataset.self,t=(el.dataset.targets||'').split(',').filter(Boolean);hi(s,t);draw(s,t,el.dataset.kind);}));
board.addEventListener('mouseleave',reset);window.addEventListener('resize',reset);
/* ── 노트북 클릭 → 새 탭으로 Colab 열기 ───────────────────────────────
Colab은 로컬/임의 URL을 직접 못 열어 GitHub에 올라간 노트북만 열 수 있다.
아래 GH(owner/repo/branch)를 본인 저장소로 채우면 클릭 시 해당 .ipynb가 Colab에 뜬다. */
const GH={owner:'scvcoder',repo:'koai-colab',branch:'main',dir:''}; // 노트북 = 저장소 루트
const NBFILE={
'01':'01_titanic_randomforest_colab.ipynb','02':'02_titanic_lightgbm_colab.ipynb',
'03':'03_house-prices_xgboost_colab.ipynb','04':'04_house-prices_catboost_colab.ipynb',
'05':'05_house-prices_ensemble_colab.ipynb','06':'06_house-prices_kfold-lightgbm_colab.ipynb',
'07':'07_nlp-getting-started_ridge_colab.ipynb','08':'08_digit-recognizer_cnn_colab.ipynb',
'09':'09_data-science-bowl-2018_unet_colab.ipynb','10':'10_dogs-vs-cats_resnet_colab.ipynb',
'11':'11_dogs-vs-cats_clip-zeroshot_colab.ipynb','12':'12_nlp-getting-started_bert_colab.ipynb',
'13':'13_digit-recognizer_frozen-knn-incremental_colab.ipynb',
'14':'14_house-prices_feature-engineering-linreg_colab.ipynb',
'15':'15_digit-recognizer_conditional-vae-generate-verify_colab.ipynb',
'16':'16_nlp-getting-started_contrastive-embedding-mnr-hardneg_colab.ipynb',
'17':'17_data-science-bowl-2018_unet-weighted-ce_colab.ipynb',
'18':'18_digit-recognizer_metadata-film-cyclic_colab.ipynb',
'19':'19_dont-call-me-turkey_audio-spectrogram-cnn_colab.ipynb',
'20':'20_nlp-getting-started_char-bilstm-segmentation_colab.ipynb',
'21':'21_mipt-ner_bilstm-ner_colab.ipynb',
'22':'22_dogs-vs-cats_clip-box-search_colab.ipynb',
'23':'23_digit-recognizer_semi-supervised_colab.ipynb',
'24':'24_data-science-bowl-2018_density-counting_colab.ipynb',
'25':'25_radar-ioai-2025_weighted-ce-unet_colab.ipynb',
'26':'26_restroom-ioai-2025_clip-reid-matching_colab.ipynb',
};
function colabURL(no){const f=NBFILE[no];if(!f)return null;
const path=[GH.dir,f].filter(Boolean).join('/');
return `https://colab.research.google.com/github/${GH.owner}/${GH.repo}/blob/${GH.branch}/${path}`;}
NB.forEach(n=>{const el=document.getElementById('node_'+n.id);if(!el)return;
el.title=el.title+'\n\n🔗 클릭 → 새 탭에서 Colab 열기';
el.addEventListener('click',()=>{
if(GH.owner==='OWNER'){alert('Colab 저장소가 아직 설정되지 않았습니다.\nioai_mapping.html 상단의 GH(owner/repo/branch)를 본인 GitHub 저장소로 채워주세요.');return;}
const u=colabURL(n.no);if(u)window.open(u,'_blank','noopener');
});
});
</script>
</body>
</html>