LoRA

로라

A lightweight fine-tuning method that trains only a small set of added weights on top of a frozen base model to reproduce a style or subject.

Also known as: Low-Rank AdaptationStyle adapter
···
html
<div class="wrap"><canvas id="cv"></canvas><span class="tag" id="tag">LoRA: 파스텔</span></div>
css
.wrap{position:relative;width:100%;height:100%}
#cv{position:absolute;inset:0;width:100%;height:100%}
.tag{position:absolute;top:8px;left:8px;z-index:2;font-size:10.5px;font-weight:700;color:#f1f0ec;
  padding:5px 9px;border-radius:8px;background:rgba(13,13,18,0.55);border:1px solid rgba(255,255,255,0.15)}
js
const cv = document.getElementById('cv'), ctx = cv.getContext('2d');
const tag = document.getElementById('tag');
function fit() { const dpr = Math.min(devicePixelRatio || 1, 2); cv.width = innerWidth * dpr; cv.height = innerHeight * dpr; ctx.setTransform(dpr, 0, 0, dpr, 0, 0); }
addEventListener('resize', fit); fit();

function mulberry32(a) { return function () { a |= 0; a = a + 0x6D2B79F5 | 0; let t = Math.imul(a ^ a >>> 15, 1 | a); t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t; return ((t ^ t >>> 14) >>> 0) / 4294967296; }; }

let layers = [], sun = null;
function computeLayout(w, h) {
  const r = mulberry32(21);
  layers = [];
  for (let l = 0; l < 3; l++) {
    const pts = [];
    for (let i = 0; i <= 7; i++) { const x = w * i / 7; const y = h * (0.5 + l * 0.14) - r() * h * 0.12; pts.push([x, y]); }
    layers.push(pts);
  }
  sun = [w * (0.3 + r() * 0.4), h * (0.2 + r() * 0.1)];
}

function drawPath(pts, h) { ctx.beginPath(); ctx.moveTo(0, h); pts.forEach(([x, y]) => ctx.lineTo(x, y)); ctx.lineTo(pts[pts.length - 1][0], h); ctx.closePath(); }

function stylePastel(w, h) {
  ctx.fillStyle = 'hsl(210 40% 88%)'; ctx.fillRect(0, 0, w, h);
  ctx.beginPath(); ctx.arc(sun[0], sun[1], Math.min(w, h) * 0.08, 0, Math.PI * 2); ctx.fillStyle = 'hsl(45 70% 80%)'; ctx.fill();
  layers.forEach((pts, l) => { drawPath(pts, h); ctx.fillStyle = 'hsl(' + (200 - l * 30) + ' 35% ' + (75 - l * 15) + '%)'; ctx.fill(); });
}
function styleInk(w, h) {
  ctx.fillStyle = '#111116'; ctx.fillRect(0, 0, w, h);
  ctx.strokeStyle = '#f1f0ec'; ctx.lineWidth = 1.4;
  ctx.beginPath(); ctx.arc(sun[0], sun[1], Math.min(w, h) * 0.08, 0, Math.PI * 2); ctx.stroke();
  layers.forEach((pts, l) => {
    ctx.beginPath(); ctx.moveTo(pts[0][0], pts[0][1]); pts.forEach(([x, y]) => ctx.lineTo(x, y)); ctx.stroke();
    ctx.strokeStyle = 'rgba(241,240,236,' + (0.35 - l * 0.08) + ')'; ctx.lineWidth = 0.8;
    for (let x = 0; x < w; x += 10) { ctx.beginPath(); ctx.moveTo(x, h * (0.62 + l * 0.14)); ctx.lineTo(x - 10, h); ctx.stroke(); }
    ctx.strokeStyle = '#f1f0ec'; ctx.lineWidth = 1.4;
  });
}
function styleNeon(w, h) {
  ctx.fillStyle = '#08060f'; ctx.fillRect(0, 0, w, h);
  ctx.shadowColor = 'hsl(300 90% 60%)'; ctx.shadowBlur = 14;
  ctx.strokeStyle = 'hsl(300 90% 65%)'; ctx.lineWidth = 2;
  ctx.beginPath(); ctx.arc(sun[0], sun[1], Math.min(w, h) * 0.08, 0, Math.PI * 2); ctx.stroke();
  layers.forEach((pts, l) => { ctx.shadowColor = 'hsl(' + (180 + l * 40) + ' 90% 60%)'; ctx.strokeStyle = 'hsl(' + (180 + l * 40) + ' 90% 65%)'; ctx.beginPath(); ctx.moveTo(pts[0][0], pts[0][1]); pts.forEach(([x, y]) => ctx.lineTo(x, y)); ctx.stroke(); });
  ctx.shadowBlur = 0;
}
const styles = [{ fn: stylePastel, name: '파스텔' }, { fn: styleInk, name: '잉크 라인' }, { fn: styleNeon, name: '네온 글로우' }];
let si = 0;
function render() {
  const w = innerWidth, h = innerHeight;
  computeLayout(w, h);
  styles[si].fn(w, h);
  tag.textContent = 'LoRA: ' + styles[si].name;
}
render();
setInterval(() => { si = (si + 1) % styles.length; render(); }, 2600);

LoRA (Low-Rank Adaptation) freezes the base model's weights and adds a small pair of low-rank matrices to each layer, training only that small set of parameters. It needs far less data and compute than retraining the whole base model, so a particular style or character can be captured from a fairly small set of images.

At generation time, a LoRA can be attached to or detached from the base model, usually with a weight dial for how strongly it should apply. Multiple LoRAs can be stacked at once, combining a style and a character together, for example.

Because it is closer to an addition on top of the base model, the base's overall compositional and anatomical tendencies stay largely intact, while "how it's rendered" leans toward the LoRA. Stacking several or setting the weight too high can make them fight each other or overpower the prompt.

The demo below is a simulation, not real weight injection — it computes one fixed composition from identical coordinates and only changes the rendering technique (flat fill, ink outline, neon glow) each cycle, showing "structure fixed, style swapped."

When to use

Use it to reproduce a particular style or character repeatedly without spelling it out in the prompt every time. Keep in mind the same LoRA can behave differently if the base model underneath it changes.