From a1a3f7b73c259d5487f6c03d632dc6f95e82fb51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Faruk=20Demirel?= Date: Sat, 28 Mar 2026 11:49:12 +0300 Subject: [PATCH 1/3] article: linear model --- .../content/GradientVectorScene.astro | 325 ++++++++++++++ src/components/content/HyperplaneScene.astro | 318 +++++++++++++ src/components/content/Scene3D.astro | 285 ++++++++++++ src/content/articles/tr/en-kucuk-kareler.mdx | 267 +++++++++++ src/utils/scene3d.ts | 418 ++++++++++++++++++ 5 files changed, 1613 insertions(+) create mode 100644 src/components/content/GradientVectorScene.astro create mode 100644 src/components/content/HyperplaneScene.astro create mode 100644 src/components/content/Scene3D.astro create mode 100644 src/content/articles/tr/en-kucuk-kareler.mdx create mode 100644 src/utils/scene3d.ts diff --git a/src/components/content/GradientVectorScene.astro b/src/components/content/GradientVectorScene.astro new file mode 100644 index 0000000..3681f3a --- /dev/null +++ b/src/components/content/GradientVectorScene.astro @@ -0,0 +1,325 @@ +--- +interface Vector3 { + x: number + y: number + z: number +} +interface PointInput { + position: Vector3 + color?: string + label?: string + size?: number +} +interface VectorInput { + origin?: Vector3 + direction: Vector3 + color?: string + length?: number + label?: string +} +interface LineInput { + from: Vector3 + to: Vector3 + color?: string + dashed?: boolean +} + +interface Props { + id?: string + height?: number + gradientDirection: Vector3 + gradientOrigin?: Vector3 + gradientColor?: string + gradientLength?: number + gradientLabel?: string + showPlane?: boolean + planeCenter?: Vector3 + planeNormal?: Vector3 + planeColor?: string + planeOpacity?: number + planeSize?: number + points?: PointInput[] + vectors?: VectorInput[] + lines?: LineInput[] + showGrid?: boolean + showAxes?: boolean + axisRange?: number + title?: string + xAxisLabel?: string + yAxisLabel?: string + zAxisLabel?: string +} + +const { + id = '', + height = 400, + gradientDirection, + gradientOrigin = { x: 0, y: 0, z: 0 }, + gradientColor = '#22c55e', + gradientLength, + gradientLabel = '∇f', + showPlane = false, + planeCenter = { x: 0, y: 0, z: 0 }, + planeNormal = { x: 0, y: 1, z: 0 }, + planeColor = '#71717a', + planeOpacity = 0.2, + planeSize, + points = [], + vectors = [], + lines = [], + showGrid = true, + showAxes = true, + axisRange = 5, + title = '', + xAxisLabel = 'X₁', + yAxisLabel = 'Y', + zAxisLabel = 'X₂', +} = Astro.props + +const chartId = `gradient-${id || Math.random().toString(36).slice(2, 9)}` +--- + +
+
+
+ + + +
+
+ + diff --git a/src/components/content/HyperplaneScene.astro b/src/components/content/HyperplaneScene.astro new file mode 100644 index 0000000..c5e639e --- /dev/null +++ b/src/components/content/HyperplaneScene.astro @@ -0,0 +1,318 @@ +--- +interface Vector3 { + x: number + y: number + z: number +} +interface AxisRange { + min: number + max: number +} +interface PointInput { + position: Vector3 + color?: string + label?: string +} +interface LineInput { + from: Vector3 + to: Vector3 + color?: string + dashed?: boolean +} + +interface Props { + id?: string + height?: number + points?: PointInput[] + planeNormal: Vector3 + planePoint: Vector3 + planeColor?: string + planeOpacity?: number + showResiduals?: boolean + residualColor?: string + residualDashed?: boolean + lines?: LineInput[] + showGrid?: boolean + xRange?: AxisRange + yRange?: AxisRange + zRange?: AxisRange + title?: string + xAxisLabel?: string + yAxisLabel?: string + zAxisLabel?: string +} + +const { + id = '', + height = 400, + points = [], + planeNormal, + planePoint, + planeColor = '#3b82f6', + planeOpacity = 0.3, + showResiduals = true, + residualColor = '#ef4444', + residualDashed = true, + lines = [], + showGrid = true, + xRange, + yRange, + zRange, + title = '', + xAxisLabel = 'X₁', + yAxisLabel = 'Y', + zAxisLabel = 'X₂', +} = Astro.props + +const chartId = `hyperplane-${id || Math.random().toString(36).slice(2, 9)}` +--- + +
+
+
+ + + +
+
+ + diff --git a/src/components/content/Scene3D.astro b/src/components/content/Scene3D.astro new file mode 100644 index 0000000..5f028aa --- /dev/null +++ b/src/components/content/Scene3D.astro @@ -0,0 +1,285 @@ +--- +interface Vector3 { + x: number + y: number + z: number +} +interface PointConfig { + position: Vector3 + color?: string + size?: number + label?: string + opacity?: number +} +interface VectorConfig { + origin?: Vector3 + direction: Vector3 + color?: string + length?: number + label?: string +} +interface LineConfig { + from: Vector3 + to: Vector3 + color?: string + dashed?: boolean +} +interface PlaneConfig { + center: Vector3 + normal: Vector3 + size?: number + color?: string + opacity?: number + label?: string +} + +interface Props { + id?: string + height?: number + points?: PointConfig[] + vectors?: VectorConfig[] + lines?: LineConfig[] + planes?: PlaneConfig[] + showGrid?: boolean + showAxes?: boolean + axisRange?: number + title?: string + xAxisLabel?: string + yAxisLabel?: string + zAxisLabel?: string +} + +const { + id = '', + height = 400, + points = [], + vectors = [], + lines = [], + planes = [], + showGrid = true, + showAxes = true, + axisRange = 5, + title = '', + xAxisLabel = 'X', + yAxisLabel = 'Y', + zAxisLabel = 'Z', +} = Astro.props + +const chartId = `scene3d-${id || Math.random().toString(36).slice(2, 9)}` +--- + +
+
+
+ + + +
+
+ + diff --git a/src/content/articles/tr/en-kucuk-kareler.mdx b/src/content/articles/tr/en-kucuk-kareler.mdx new file mode 100644 index 0000000..27ea7b5 --- /dev/null +++ b/src/content/articles/tr/en-kucuk-kareler.mdx @@ -0,0 +1,267 @@ +--- +lang: 'tr' +slug: 'en-kucuk-kareler' +title: 'Least Squares Nedir?' +excerpt: 'Linear regression etiketlenmiş veri setlerinden doğrusal ilişkiyi modellemek için kullanılan supervised makine öğrenmesi algoritmasıdır' +category: 'machine-learning' +tags: ['linear-regression', 'supervised-learning', 'İmplementasyon'] +author: '@omerfdmrl' +date: '2026-03-26' +views: 0 +status: 'Published' +--- + +import HyperplaneScene from '../../../components/content/HyperplaneScene.astro' +import GradientVectorScene from '../../../components/content/GradientVectorScene.astro' + +Lineer modeller hem istatistik hem makine öğrenmesinde son 30 yıldır çok önemli bir alana sahip. +Verilen input vektörü ile $X^T = (X_1, X_2, X_3 ...)$ output değeri $Y$'yi tahmin etmeye çalışırız. + +$$ +\hat{Y} = \hat{\beta}_0 + \sum_{j=1}^p X_j \hat{\beta}_j +$$ + +$\hat{\beta}_0$ **bias (intercept)** değeridir. Bu formülü $X$'i vektör olarak yazarsak: + +$$ +\hat{Y} = X^{\top} \hat{\beta} +$$ + +olarak kullanabiliriz. + +## Dot Product Anlamak + +Bunu daha iyi anlamak için formülü tek tek açalım: + +$$ +\hat{Y}=\hat{\beta}_0 + X_1\hat{\beta}_1 + X_2\hat{\beta}_2 + \cdots + X_p\hat{\beta}_p +$$ + +Burada her $X_j$ tek bir feature değeridir ve tek sayıdır: + +$$ +X_j \in \mathbb{R} +$$ + +Aynı şekilde her katsayı da tek sayıdır: + +$$ +\hat{\beta}_j \in \mathbb{R} +$$ + +Dolayısıyla iki reel sayının çarpımı yine tek bir reel sayı verir: + +$$ +X_j \hat{\beta}_j \in \mathbb{R} +$$ + +Yani örneğin: + +$$ +X_1=3,\quad \hat{\beta}_1=4 +$$ + +ise: + +$$ +X_1\hat{\beta}_1 = 12 +$$ + +elde edilir. + +Bu 12 artık bir **skaler**dir; yani vektör veya matris değil, yalnızca tek bir sayıdır. + +Aynı durum tüm terimler için geçerlidir: + +$$ +X_2\hat{\beta}_2,\; X_3\hat{\beta}_3,\; \ldots,\; X_p\hat{\beta}_p +$$ + +hepsi tek tek skaler değerler üretir. + +Sonuçta bunların toplamı da yine tek bir sayı olur: + +$$ +\hat{Y}\in \mathbb{R} +$$ + +Bu nedenle lineer modelin çıktısı tek bir skaler tahmindir. +Dolayısıyla her çarpım da skaler olur, ve buna **dot product (inner product)** nedir. + +## Hiperdüzlem (Hyperplane) + +Lineer modelin geometrik anlamı bir **hiperdüzlem**dir. İki feature ($X_1, X_2$) ve bir output ($Y$) ile düşünelim. +Modelin tahmin ettiği değerler bir düzlem üzerinde yer alır. Noktaların düzleme olan dik uzaklıkları **residual (artık)** değerleridir +ve en küçük kareler yöntemi bu residual'ların karelerinin toplamını minimize eder. + +Aşağıdaki görselleştirmede mavi düzlem öğrenilen hiperdüzlemi, noktalar gerçek veri noktalarını ve kırmızı kesikli çizgiler residual'ları gösterir: + + + +Her nokta, hiperdüzleme dikey olarak projekte edilir. Bu dik uzaklık residual'dür: +$e_i = Y_i - \hat{Y}_i$. En küçük kareler, $\sum e_i^2$'yi minimize eden $\hat{\beta}$ katsayılarını bulur. + +### Gradient Vektörü + +Optimizasyon sürecinde **gradient** kavramı çok önemlidir. Bir fonksiyonun gradyanı, o noktada fonksiyonun en dik artış yönünü gösterir: + +$$f'(X) = \nabla f(X) = \beta$$ + +Bu vektör, input uzayında en dik yokuş yukarı yönü gösterir. Gradient descent algoritması bu vektörün tersi yönde ilerleyerek minimumu bulur: + + + +Yukarıdaki görselleştirmede yeşil ok $\nabla f$ vektörünü, turuncu nokta ise mevcut konum $X$'i temsil eder. +Gradient, bu noktada fonksiyonun en hızlı arttığı yönü gösterir. + +### Bias'ı Dahil Etmek + +Bias' ayrı yazmak yerine feature vektörünü genişletebiliriz: + +$$ +X = +\begin{bmatrix} +1 \\ X_1 \\ X_2 \\ \vdots \\ X_p +\end{bmatrix}, +\quad +\hat{\beta} = +\begin{bmatrix} +\hat{\beta}_0 \\ \hat{\beta}_1 \\ \hat{\beta}_2 \\ \vdots \\ \hat{\beta}_p +\end{bmatrix} +$$ + +Burada 1 ile başlatma sebebimiz, formüldeki $\hat{\beta}_0$ değerini de ekleyebilmek için. + +### Neden Transpose Gerekiyor? + +İki sütun vektörünü doğrudan çarpamayız. + +$$ +X = +\begin{bmatrix} +1 \\ X_1 \\ X_2 \\ \vdots \\ X_p +\end{bmatrix} += +(p + 1)\times1, +\quad +\hat{\beta} = +\begin{bmatrix} +\hat{\beta}_0 \\ \hat{\beta}_1 \\ \hat{\beta}_2 \\ \vdots \\ \hat{\beta}_p +\end{bmatrix} = +(p + 1)\times1 +$$ + +Dot product çarpımında içerikde boyutların aynı olması gerekir. Bu yüzden de birini satır olarak yazmamız gerekir. + +$$ +((p + 1)\times1) \times ((p + 1)\times1) \rightarrow (1\times(p + 1)) \times ((p + 1)\times1) +$$ + +$$ += 1\times1 +$$ + +Yani artık: + +$$ +X = +\begin{bmatrix} +1 & X_1 & X_2 & \ldots & X_p +\end{bmatrix} += +1\times(p + 1), +\quad +\hat{\beta} = +\begin{bmatrix} +\hat{\beta}_0 \\ \hat{\beta}_1 \\ \hat{\beta}_2 \\ \vdots \\ \hat{\beta}_p +\end{bmatrix} = +(p + 1)\times1 +$$ + +### Örnek Çözüm + +İki feature olsun: + +$$ +X= +\begin{bmatrix} +1\\ +3\\ +5 +\end{bmatrix} +,\quad +\hat{\beta}= +\begin{bmatrix} +2\\ +4\\ +6 +\end{bmatrix} +$$ + +O zaman: + +$$ +X^\top \hat{\beta} += +\begin{bmatrix} +1 & 3 & 5 +\end{bmatrix} +\begin{bmatrix} +2\\ +4\\ +6 +\end{bmatrix} +$$ + +Sonuç: + +$$ +=1\cdot2 + 3\cdot4 + 5\cdot6 +$$ + +$$ +=44 +$$ diff --git a/src/utils/scene3d.ts b/src/utils/scene3d.ts new file mode 100644 index 0000000..3b931a6 --- /dev/null +++ b/src/utils/scene3d.ts @@ -0,0 +1,418 @@ +import * as THREE from 'three' +import { OrbitControls } from 'three/addons/controls/OrbitControls.js' + +export interface Vec3 { + x: number + y: number + z: number +} + +export interface ThemeColors { + textColor: number + gridColor: number + axisColor: number + bgCss: string + darkText: string + lightText: string +} + +export function getThemeColors3D(): ThemeColors { + const isDark = document.documentElement.classList.contains('dark') + return { + textColor: isDark ? 0xd4d4d8 : 0x52525b, + gridColor: isDark ? 0x3f3f46 : 0xdde1e7, + axisColor: isDark ? 0x71717a : 0x94a3b8, + bgCss: isDark ? '#0c0c0e' : '#f8fafc', + darkText: isDark ? '#e4e4e7' : '#27272a', + lightText: isDark ? '#a1a1aa' : '#71717a', + } +} + +export function makeLabel( + text: string, + position: THREE.Vector3, + colorHex: number, + scaleW = 0.28, + scaleH = 0.1, + fontPx = 28, +): THREE.Sprite { + const canvas = document.createElement('canvas') + canvas.width = 256 + canvas.height = 64 + const ctx = canvas.getContext('2d')! + ctx.clearRect(0, 0, 256, 64) + ctx.fillStyle = `#${colorHex.toString(16).padStart(6, '0')}` + ctx.font = `${fontPx}px monospace` + ctx.textAlign = 'center' + ctx.textBaseline = 'middle' + ctx.fillText(text, 128, 32) + const texture = new THREE.CanvasTexture(canvas) + texture.needsUpdate = true + const mat = new THREE.SpriteMaterial({ map: texture, transparent: true, depthTest: false }) + const sprite = new THREE.Sprite(mat) + sprite.position.copy(position) + sprite.scale.set(scaleW, scaleH, 1) + return sprite +} + +export function createBaseScene(): THREE.Scene { + const scene = new THREE.Scene() + scene.add(new THREE.AmbientLight(0xffffff, 0.7)) + const dLight = new THREE.DirectionalLight(0xffffff, 0.9) + dLight.position.set(3, 5, 3) + scene.add(dLight) + const dLight2 = new THREE.DirectionalLight(0xffffff, 0.25) + dLight2.position.set(-2, -1, -2) + scene.add(dLight2) + return scene +} + +export function createCamera( + width: number, + height: number, + position: THREE.Vector3, + target: THREE.Vector3, +): THREE.PerspectiveCamera { + const camera = new THREE.PerspectiveCamera(40, width / height, 0.01, 200) + camera.position.copy(position) + camera.lookAt(target) + return camera +} + +export function createRenderer(container: HTMLElement, width: number, height: number): THREE.WebGLRenderer { + const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }) + renderer.setSize(width, height) + renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)) + renderer.setClearColor(0x000000, 0) + renderer.domElement.style.cssText = 'position:absolute;top:0;left:0;z-index:1;border-radius:8px;' + container.style.position = 'relative' + container.appendChild(renderer.domElement) + return renderer +} + +export function createControls( + camera: THREE.PerspectiveCamera, + element: HTMLElement, + target: THREE.Vector3, + cubeSize: number, +): OrbitControls { + const controls = new OrbitControls(camera, element) + controls.enableDamping = true + controls.dampingFactor = 0.06 + controls.rotateSpeed = 0.7 + controls.zoomSpeed = 1.2 + controls.minDistance = cubeSize * 0.5 + controls.maxDistance = cubeSize * 8 + controls.target.copy(target) + controls.update() + return controls +} + +export function createAxisArrows( + cube: number, + theme: ThemeColors, + scene: THREE.Scene, + labels: { x: string; y: string; z: string }, + tickLabels: { x: string[]; y: string[]; z: string[] }, +): void { + const arrowLen = cube + 0.15 + const headLen = 0.08 + const headWid = 0.04 + + const xDir = new THREE.Vector3(1, 0, 0) + const yDir = new THREE.Vector3(0, 1, 0) + const zDir = new THREE.Vector3(0, 0, 1) + + scene.add(new THREE.ArrowHelper(xDir, new THREE.Vector3(0, 0, 0), arrowLen, theme.axisColor, headLen, headWid)) + scene.add(new THREE.ArrowHelper(yDir, new THREE.Vector3(0, 0, 0), arrowLen, theme.axisColor, headLen, headWid)) + scene.add(new THREE.ArrowHelper(zDir, new THREE.Vector3(0, 0, 0), arrowLen, theme.axisColor, headLen, headWid)) + + const tickOffset = cube * 0.02 + + scene.add( + makeLabel( + labels.x, + new THREE.Vector3(cube / 2, -tickOffset * 2.5, cube + tickOffset * 2), + theme.textColor, + 0.3, + 0.1, + 30, + ), + ) + scene.add( + makeLabel( + labels.y, + new THREE.Vector3(-tickOffset * 3, cube / 2, cube + tickOffset * 2), + theme.textColor, + 0.3, + 0.1, + 30, + ), + ) + scene.add( + makeLabel( + labels.z, + new THREE.Vector3(-tickOffset * 3, -tickOffset * 2.5, cube / 2), + theme.textColor, + 0.3, + 0.1, + 30, + ), + ) + + for (let i = 0; i < tickLabels.x.length; i++) { + const t = i / (tickLabels.x.length - 1) + const pos = t * cube + scene.add( + makeLabel( + tickLabels.x[i], + new THREE.Vector3(pos, -tickOffset, cube + tickOffset), + theme.textColor, + 0.2, + 0.08, + 24, + ), + ) + } + for (let i = 0; i < tickLabels.y.length; i++) { + const t = i / (tickLabels.y.length - 1) + const pos = t * cube + scene.add( + makeLabel( + tickLabels.y[i], + new THREE.Vector3(-tickOffset * 1.5, pos, cube + tickOffset), + theme.textColor, + 0.2, + 0.08, + 24, + ), + ) + } + for (let i = 0; i < tickLabels.z.length; i++) { + const t = i / (tickLabels.z.length - 1) + const pos = t * cube + scene.add( + makeLabel( + tickLabels.z[i], + new THREE.Vector3(-tickOffset * 1.5, -tickOffset, pos), + theme.textColor, + 0.2, + 0.08, + 24, + ), + ) + } +} + +export function createGridLines(cube: number, theme: ThemeColors, scene: THREE.Scene, divisions = 6): void { + function makeFaceGrid(origin: THREE.Vector3, uDir: THREE.Vector3, vDir: THREE.Vector3): void { + const points: THREE.Vector3[] = [] + for (let i = 0; i <= divisions; i++) { + const t = (i / divisions) * cube + points.push(origin.clone().addScaledVector(vDir, t)) + points.push(origin.clone().addScaledVector(uDir, cube).addScaledVector(vDir, t)) + points.push(origin.clone().addScaledVector(uDir, t)) + points.push(origin.clone().addScaledVector(uDir, t).addScaledVector(vDir, cube)) + } + const geo = new THREE.BufferGeometry().setFromPoints(points) + const mat = new THREE.LineBasicMaterial({ color: theme.gridColor, transparent: true, opacity: 0.35 }) + scene.add(new THREE.LineSegments(geo, mat)) + } + + makeFaceGrid(new THREE.Vector3(0, 0, 0), new THREE.Vector3(1, 0, 0), new THREE.Vector3(0, 0, 1)) + makeFaceGrid(new THREE.Vector3(0, 0, 0), new THREE.Vector3(1, 0, 0), new THREE.Vector3(0, 1, 0)) + makeFaceGrid(new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 0, 1), new THREE.Vector3(0, 1, 0)) +} + +export function renderPoint( + scene: THREE.Scene, + position: THREE.Vector3, + color: number, + size = 8, + opacity = 1, +): THREE.Mesh { + const geo = new THREE.SphereGeometry(size * 0.005, 20, 20) + const mat = new THREE.MeshStandardMaterial({ + color, + transparent: opacity < 1, + opacity, + roughness: 0.35, + metalness: 0.15, + }) + const mesh = new THREE.Mesh(geo, mat) + mesh.position.copy(position) + scene.add(mesh) + return mesh +} + +export function renderVector( + scene: THREE.Scene, + origin: THREE.Vector3, + direction: THREE.Vector3, + color: number, + length?: number, +): THREE.ArrowHelper { + const len = length ?? direction.length() + const arrow = new THREE.ArrowHelper(direction.clone().normalize(), origin, len, color, len * 0.15, len * 0.06) + scene.add(arrow) + return arrow +} + +export function renderLine( + scene: THREE.Scene, + from: THREE.Vector3, + to: THREE.Vector3, + color: number, + dashed = false, +): THREE.Line | THREE.LineSegments { + if (dashed) { + const geo = new THREE.BufferGeometry().setFromPoints([from, to]) + const mat = new THREE.LineDashedMaterial({ + color, + dashSize: 0.05, + gapSize: 0.03, + transparent: true, + opacity: 0.8, + }) + const line = new THREE.Line(geo, mat) + line.computeLineDistances() + scene.add(line) + return line + } + const geo = new THREE.BufferGeometry().setFromPoints([from, to]) + const mat = new THREE.LineBasicMaterial({ color, transparent: true, opacity: 0.8 }) + const line = new THREE.Line(geo, mat) + scene.add(line) + return line +} + +export function renderPlane( + scene: THREE.Scene, + center: THREE.Vector3, + normal: THREE.Vector3, + size: number, + color: number, + opacity = 0.35, +): THREE.Mesh { + const geo = new THREE.PlaneGeometry(size, size) + const mat = new THREE.MeshStandardMaterial({ + color, + transparent: true, + opacity, + side: THREE.DoubleSide, + roughness: 0.6, + metalness: 0.1, + }) + const mesh = new THREE.Mesh(geo, mat) + mesh.position.copy(center) + + const up = new THREE.Vector3(0, 1, 0) + const norm = normal.clone().normalize() + if (Math.abs(norm.dot(up)) > 0.999) { + mesh.rotation.x = norm.y > 0 ? -Math.PI / 2 : Math.PI / 2 + } else { + mesh.lookAt(center.clone().add(norm)) + } + scene.add(mesh) + return mesh +} + +export function projectPointOnPlane( + point: THREE.Vector3, + planeNormal: THREE.Vector3, + planePoint: THREE.Vector3, +): THREE.Vector3 { + const n = planeNormal.clone().normalize() + const diff = point.clone().sub(planePoint) + const dist = diff.dot(n) + return point.clone().sub(n.clone().multiplyScalar(dist)) +} + +export function computeResidual( + point: THREE.Vector3, + planeNormal: THREE.Vector3, + planePoint: THREE.Vector3, +): { from: THREE.Vector3; to: THREE.Vector3 } { + return { + from: point.clone(), + to: projectPointOnPlane(point, planeNormal, planePoint), + } +} + +export function setupZoomControls( + container: HTMLElement, + camera: THREE.PerspectiveCamera, + controls: OrbitControls, + defaultEye: THREE.Vector3, + center: THREE.Vector3, +): void { + const duration = 300 + const zoomFactor = 0.75 + + container.querySelectorAll('.chart-btn').forEach((btn) => { + btn.addEventListener('click', () => { + if ((btn as HTMLElement).classList.contains('zoom-in')) { + const targetPos = camera.position + .clone() + .sub(controls.target) + .normalize() + .multiplyScalar(camera.position.distanceTo(controls.target) * zoomFactor) + .add(controls.target) + const startPos = camera.position.clone() + const zoomStart = performance.now() + function zoomInAnim() { + const p = Math.min((performance.now() - zoomStart) / duration, 1) + const e = 1 - Math.pow(1 - p, 3) + camera.position.lerpVectors(startPos, targetPos, e) + controls.update() + if (p < 1) requestAnimationFrame(zoomInAnim) + } + zoomInAnim() + } else if ((btn as HTMLElement).classList.contains('zoom-out')) { + const targetPos = camera.position + .clone() + .sub(controls.target) + .normalize() + .multiplyScalar(camera.position.distanceTo(controls.target) / zoomFactor) + .add(controls.target) + const startPos = camera.position.clone() + const zoomStart = performance.now() + function zoomOutAnim() { + const p = Math.min((performance.now() - zoomStart) / duration, 1) + const e = 1 - Math.pow(1 - p, 3) + camera.position.lerpVectors(startPos, targetPos, e) + controls.update() + if (p < 1) requestAnimationFrame(zoomOutAnim) + } + zoomOutAnim() + } else { + const resetStart = performance.now() + const fromPos = camera.position.clone() + const toPos = defaultEye.clone() + function resetAnim() { + const p = Math.min((performance.now() - resetStart) / 400, 1) + const e = 1 - Math.pow(1 - p, 3) + camera.position.lerpVectors(fromPos, toPos, e) + controls.target.copy(center) + controls.update() + if (p < 1) requestAnimationFrame(resetAnim) + } + resetAnim() + } + }) + }) +} + +export function createControlsHTML(): string { + return `
+ + + +
` +} From ea38bf717e109512f1f0e7763e0cf97cf28e4c1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Faruk=20Demirel?= Date: Sun, 29 Mar 2026 19:11:43 +0300 Subject: [PATCH 2/3] article: linear model refactored --- ...cuk-kareler.mdx => linear-model-nedir.mdx} | 158 +++++++++--------- 1 file changed, 82 insertions(+), 76 deletions(-) rename src/content/articles/tr/{en-kucuk-kareler.mdx => linear-model-nedir.mdx} (63%) diff --git a/src/content/articles/tr/en-kucuk-kareler.mdx b/src/content/articles/tr/linear-model-nedir.mdx similarity index 63% rename from src/content/articles/tr/en-kucuk-kareler.mdx rename to src/content/articles/tr/linear-model-nedir.mdx index 27ea7b5..9b61ef1 100644 --- a/src/content/articles/tr/en-kucuk-kareler.mdx +++ b/src/content/articles/tr/linear-model-nedir.mdx @@ -1,12 +1,12 @@ --- lang: 'tr' -slug: 'en-kucuk-kareler' -title: 'Least Squares Nedir?' -excerpt: 'Linear regression etiketlenmiş veri setlerinden doğrusal ilişkiyi modellemek için kullanılan supervised makine öğrenmesi algoritmasıdır' +slug: 'linear-model-nedir' +title: 'Linear Model Nedir?' +excerpt: 'Lineer model, etiketlenmiş veri setlerindeki girdi ve çıktı değişkenleri arasındaki doğrusal ilişkiyi matematiksel olarak modellemek için kullanılan temel bir supervised (gözetimli) makine öğrenmesi algoritmasıdır.' category: 'machine-learning' -tags: ['linear-regression', 'supervised-learning', 'İmplementasyon'] +tags: ['linear-regression', 'supervised-learning', 'teori'] author: '@omerfdmrl' -date: '2026-03-26' +date: '2026-03-29' views: 0 status: 'Published' --- @@ -14,8 +14,9 @@ status: 'Published' import HyperplaneScene from '../../../components/content/HyperplaneScene.astro' import GradientVectorScene from '../../../components/content/GradientVectorScene.astro' -Lineer modeller hem istatistik hem makine öğrenmesinde son 30 yıldır çok önemli bir alana sahip. -Verilen input vektörü ile $X^T = (X_1, X_2, X_3 ...)$ output değeri $Y$'yi tahmin etmeye çalışırız. +Makine öğrenmesi ve istatistik dünyasına atılan ilk adım genellikle **Lineer Modeller** ile başlar. +Onlarca yıldır veri biliminin en temel yapı taşı olan bu yaklaşım, veriler arasındaki karmaşık gibi görünen ilişkileri basit ve doğrusal bir denklemle ifade etmemizi sağlar. +Özünde yaptığı iş çok nettir: Verilen input vektörü ile $X^T = (X_1, X_2, X_3 ...)$ output değeri $Y$'yi tahmin etmeye çalışır: $$ \hat{Y} = \hat{\beta}_0 + \sum_{j=1}^p X_j \hat{\beta}_j @@ -88,75 +89,6 @@ $$ Bu nedenle lineer modelin çıktısı tek bir skaler tahmindir. Dolayısıyla her çarpım da skaler olur, ve buna **dot product (inner product)** nedir. -## Hiperdüzlem (Hyperplane) - -Lineer modelin geometrik anlamı bir **hiperdüzlem**dir. İki feature ($X_1, X_2$) ve bir output ($Y$) ile düşünelim. -Modelin tahmin ettiği değerler bir düzlem üzerinde yer alır. Noktaların düzleme olan dik uzaklıkları **residual (artık)** değerleridir -ve en küçük kareler yöntemi bu residual'ların karelerinin toplamını minimize eder. - -Aşağıdaki görselleştirmede mavi düzlem öğrenilen hiperdüzlemi, noktalar gerçek veri noktalarını ve kırmızı kesikli çizgiler residual'ları gösterir: - - - -Her nokta, hiperdüzleme dikey olarak projekte edilir. Bu dik uzaklık residual'dür: -$e_i = Y_i - \hat{Y}_i$. En küçük kareler, $\sum e_i^2$'yi minimize eden $\hat{\beta}$ katsayılarını bulur. - -### Gradient Vektörü - -Optimizasyon sürecinde **gradient** kavramı çok önemlidir. Bir fonksiyonun gradyanı, o noktada fonksiyonun en dik artış yönünü gösterir: - -$$f'(X) = \nabla f(X) = \beta$$ - -Bu vektör, input uzayında en dik yokuş yukarı yönü gösterir. Gradient descent algoritması bu vektörün tersi yönde ilerleyerek minimumu bulur: - - - -Yukarıdaki görselleştirmede yeşil ok $\nabla f$ vektörünü, turuncu nokta ise mevcut konum $X$'i temsil eder. -Gradient, bu noktada fonksiyonun en hızlı arttığı yönü gösterir. - ### Bias'ı Dahil Etmek Bias' ayrı yazmak yerine feature vektörünü genişletebiliriz: @@ -265,3 +197,77 @@ $$ $$ =44 $$ + +## Hiperdüzlem (Hyperplane) + +Lineer modelin geometrik anlamı bir **hiperdüzlem**dir. İki feature ($X_1, X_2$) ve bir output ($Y$) ile düşünelim. +Modelimizin ürettiği tahminler ($\hat{Y}$), $X_1$ ve $X_2$ eksenleri boyunca uzanan düz bir yüzey, yani bir düzlem (veya daha yüksek boyutlarda hiperdüzlem) oluşturur. + +Aşağıdaki görselleştirmede; mavi düzlem lineer modelimizin uzayda oluşturduğu hiperdüzlemi, noktalar gerçek veri noktalarını ve kırmızı kesikli çizgiler ise gerçek verilerin bizim modelimize (düzleme) olan uzaklıklarını gösterir: + + + +Her nokta, hiperdüzleme dikey olarak izdüşürülür. Gerçek değer ($Y$) ile modelin düzlem üzerindeki tahmini ($\hat{Y}$) arasındaki bu farklara **residual (artık)** denir: +$e_i = Y_i - \hat{Y}_i$. + +> **Not:** Bu düzlemin uzaya nasıl yerleştirileceği ve aradaki bu residual hatalarının nasıl en aza indirileceği, optimizasyon yöntemlerinin konusudur. Lineer model sadece bu düzlemin matematiksel tanımıdır. + +### Katsayılar ve Gradient Vektörü + +Lineer modelin denklemini bir fonksiyon olarak, $f(X) = X^\top \beta$ şeklinde düşündüğümüzde, modelin uzaydaki eğimini belirleyen şey $\beta$ katsayılarıdır. Matematiksel olarak bir fonksiyonun **gradyanı (gradient)**, o noktada fonksiyonun en dik artış yönünü gösterir. + +Lineer bir model için bu gradyan doğrudan katsayılar vektörüne eşittir: + +$$ +f'(X) = \nabla f(X) = \beta +$$ + +Yani $\beta$ vektörü, input uzayında modelimizin çıktısının ($\hat{Y}$) en hızlı arttığı yönü gösterir. + + + +Yukarıdaki görselleştirmede yeşil ok $\nabla f$ (yani $\beta$) vektörünü, turuncu nokta ise mevcut konum $X$'i temsil eder. +Lineer modelde düzlemin eğimi sabit olduğundan, bu yön her yerde aynıdır. Modelleri eğitirken kullandığımız algoritmalar (örneğin Gradient Descent), bu vektörel özellikleri kullanarak modeli en doğru konuma getirmeye çalışır. From 4ec0c0d543232101b77dec2bee2d7655dd18ce6b Mon Sep 17 00:00:00 2001 From: omerfdmrl <62325045+omerfdmrl@users.noreply.github.com> Date: Sun, 29 Mar 2026 16:13:40 +0000 Subject: [PATCH 3/3] chore: auto-generate data and og images --- public/og/tr/linear-model-nedir.png | Bin 0 -> 35378 bytes src/data/tr/articles.json | 5 +++++ 2 files changed, 5 insertions(+) create mode 100644 public/og/tr/linear-model-nedir.png diff --git a/public/og/tr/linear-model-nedir.png b/public/og/tr/linear-model-nedir.png new file mode 100644 index 0000000000000000000000000000000000000000..f5b9a5e0b20e229329f6ff6b173dcfba0c34c67d GIT binary patch literal 35378 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 z zIqneo9&?>uje<4nK=zFCt!@*C4%3sO-Ww?6yo>$e{qTPN;hB!7XMSTO7!96vu!<{n zZjtfW$1}>});&F0W81%8y=hS+R5SfzUa6+4-G>`bvyP`j0|nv>>+S4J?o)0ixADj0 z2};Ba1I(Uh$B8yG&Yu&N`klp@D>WuH<|3*Wg;bg|w8+_C*ECp{#{+3LE_oBQ{FQfD8q*MZ3m zM#OTGldpaerAzNXDhdb=o?%m%oetQ4=h$>Zv_)BAazFHGmI->d{m}lt(hbl!8wZZn zKm+4C>1(s5j6`kcw55-t$)MTYFcdl6d@M+d#2oL};63FFO|N;OhyT(rG{rX6XgbqG znxTteG(KNgNQ|K8GfeGth;fB<-4kpBY`P%dr(_tW)bN48O+U!JR8VT)$Mst@i5X%x z;A#RU8Cm?ZG{$+RM*78`1Z~c1=<==~$g#p9z&5+G@BJ5cK+ygwh%B%|6A6 zdO^0&A45S%ME~J{IhMbraUko4IOE$1#jjAIae!RYN#ci;|J*O&M&$;0E9wz^mL59G zA+tY(dIoLGyz0bh2~ipMQjSd1c~jz;hHyIMMRkL;v8&KoDs*f7K25p^^Uxw>#XR4i z(%gIr$!U?6lQ}DVGoq0hpl0oDi@_Xa1(^)3bdYB=$f9^CO)o{5+9xXEG=z9z3V0KU zc^IrAOLI_(>v4arvst_S56_KLQc|D)xbRPpDFa~UPTRkfi0?19rM2Q)1{xv3UTINt zL&tLhQT?8GQ`fdf86Vy68|#!il$v%3vL@=yLkw2t9vQ!+c0I3EtYBNFYpl|FDW~7T z_!T}2mPd%6V`Y&}Fig_{b;(#zNhk2nZcc@M@r6v5_vTuEF^^<0@%T=d}9@f{RTRou>_L z#~n?2ZanoIryk~Y(3aye$WQJk3|U_Ls}WvkY_sm*!q@N77=P66?f^&xTXoQF=jqRB zE-V9Ct-vc+HVTiRoo7#_G>)_<>jdjyGXz^Z3FNtmA?iXkqt+JUjd7)dS;lG%`75fobNo^9 zc5;P+)N=cUt0Y-FJJxW?y}UH$5gDEOjgs}CE9cU#d0Clve|yz!US@}2v9g*<@8v*V z6CH;~PihZdv+s@=jL@=z$Xk#1s-zh$+Pb8hZF0L?t%Zx{!7$KHe~-6!JgGg6eD8ja zMsaFnVJkSw$%C0#L)`9T{g1 zRc-(YXrP5_6)nA0Uj0*NtJE#}VIb0?ei9lV{f`D>yeBSd-n)g_)>wkOv44KpzAd#b zTz>wH-S(sxgx#>g=)pUee*1HG@JN3ZMTV}kJoBeW$<#OvDtm0^lr;tGwhLKMf1-Qf z*E$Wae?lfaR}rH)WfuP%0-Ls^SJ|MzYUi|gTAJ~GuNfHiyFnEl1=!Se>};2c((1>@ zMUcMwf?%(X72kaZn(e{f4heFG_(IW?r*)_8e7pG18;*H#p} zINQOEc}y%ctJEDf2u}cK?c$rN+TCHuIhYDmKJJ3?jrlD5r(CqCC6(okL$-4%;hP^p zi#=qe*G7C7uapUq%OA*gp75Z2A^2qfzw^F+Ei zk|PLq#ylaw4Qm_#&n}q^l5be+ypE4tsJ!P1PSoP%UjWZN_unxn>ge*^U`trv!POaJ zEFsHL7wWxjB0|`?K-M{2RuldUrDP}d@CxGd4KK;IRr_pz9Cl4-o&P7-vS*Gy%0K$$ z;F;6)aNXoqTDW9elwOSPrE9SSZ$ zKIzQEuNsAhIti!3#~@0f>M~kE(3bRAkC@L-xh6FoM{efoUri;{OBUXBB;biNM5HQ54ZqUsJl1DuI#lNcI5&aZK2|lv-}lv<6#YO5Jc9 zn>|u>58tWf!nH0BD|fBRTYKY1eQi%v;R@|wyJ%~xg9FynD7eGEL%h@MiBz=Omq|F5 ze^+Nj)35-C-eSoNrHX>qe0ZA5nami(2VFt=Qa4{eg-&toi~KoeooG@c!w!6DtQQ{) zzOCxqvC@R6Q+lQnI3_=-9i4xX>0#~LU&Z;P#%{oAs>YsPNrGu76w>n@g^6ozRu*NZ zN4Mc*jMGnFU#-&}bSwKEH60p&TE^`>YT;5A2=7wl=;`OAEJZf*3fioBazHF^a^C1{ zxG507RWsM`NSi`@H$nX>y;;g<;2(~>`Y(w4Ub54puOSSb9er&1cXP;s*aIbhr6et~ z5Dux)w7rb(-216!xN)^^(AWop8n<-THo^{R1;Lb)A>W?LkCfl@qnlQ*tT#{G$h`=8 zbyDfmMcS$Ek{dbxh4J$+c#1(~;b^yDekf)kb2XP-1!U`am+?3g>0J7nU3@sd=_T)4 zdrhD7#u6fMz+F7lJl3)Za!o9*`73fu68K8K$Mz-y;S)KyQ2Q&ldOV<1@ecxz@{j<3 zx|3PFvB9O&L_A2Vh;W1Dhb8~(Nm0!jK{kYKMs=bH7~_)lL9jNGB6b+-FVa#@{Zxz zLbNgY1<9(}mDz9o=7qHTd|i!p=BgqRajJ6XC_1|_gJ64)%9^Pk8jeHP4*bY%rwN(O zzO|mb-<_&BYEidxy{f_sYCl#v-o;Bt4MmL*HF)@&nQ7swp`m`<=EvfVu7{Q?I%+)$ z`kb-GrA8jK-Up>T>aNXt8|x{ghD0^t%z+gcib1l{l+SxZ3RVmF*?G;hZ0S)cDfb`1 zT}WQ(meXw8@9kyCLut_ewqjq#k%B(pd9qzNJBHLc-|4gKpU+WaM%?#jGH)S0q4Gwv zul63LoZoR$cn-Zp+;*do?+uxBFh&8gxmp$eL&r+l29g!^yC2fcOS_@E241_jQv$&nG%Qnd1m7+xYmhRgm_Qo_r$@`z~U+-Z@wxMI=(9Zn$yX zAszzU$+5qScjsr|T55L%m;-!8a^vbpP+XECp<0&tTISephC{D1jS=S7sGj;zN#HGscK<9M+BO|p6JC%L z^>b!*qn~hdj&!{m@OT4)BOHw9+X@UT{lGS?XrfRQa@BVqiVvG=L5g~HJfnSR0nLI5 z{M?(A-r=un<0+8SKRH(soU#W-2*Q<-p2pDC8k0)%>E)ZSQ=y%iS~XjxUeyB%_tC~A zEk%dDJ;F|O*qq$z>qae_+rF>$Zt6uA>x28eM&OmpcOOS@!`0xFBuI=<8~GxvA2b+r z$I4xc!X?lWu6HLp2Ze$7(_Y$98G1u^Tj!{OkX!pR)2=;@+x!3;s>Nw*7jGO8_LEDR z4bq|AN(~FTDu(d$eAg1#CWYBGZ}%{6BtLMo0-k)zO<$N z*awifm@{Gjg8a&q>NV4Wh19T@>CO~uI@p7bWDgpUg@0^_|12VUPU94?J@?2xUQSO( z_SwDZ zW&C;mk>jANMT5fINHUyu>{-qgZDeTJV0?;!qqlb@;RY6gMQ*x&x37ag78HEhVm_b0 z)uRCwq14bgM*jhdCx13Ie84e@$zi}i+nLz4?;v9;So7Y1Y(KOWGtK#X-^%8ht-8V{0JNRPyC}`-O#(i?3Sx#0B zmlCYoa*v+%wOnW-4O&X@)>yj%b7Y(tU7iaTtn!=M?~nhrV*WT7EQzUvB>9GUeKMK{ zQBSKm66^bz&}uUV|G9x#g_A@ZXL{VWcbE=}1^g)lFa~~ScK)*VoBx6;h*wW{dKv#Q zf3iQ=YYdL9@=R8IfoqT(qP}g6d?%^&S*VK9Ho2lkADNc-+HF-rs*T+XnrE*rJN7+q zEAG_s-53L7&HDby4Q79Fr>M_a?Np_o@NNf`QEYp+Bd2r$*wszh%!E$OAgs}1BhK2w z?_&@(A%U8H_?@qK*?}l*Cnj7oB3eN{$~B-=uEEZYnrBi&RtnmnytueVeK76zkIAa^ zUkk$&VE9ht=p~dj)5v<8F~7RkzGClPq_`y0GC3yKZM9k#b~`#Q?SzN&Y^X{@RD8F+ z{E`cqtvAGM!I?sJleym>ru7T-8lt+&w^|HYmb*eZQ2x*ny*ME6Z~by<1q9pE@HJ5n zDn=rlM{K8L{qwI-dJ?`R(9OQDFUQQ<>x6rIKPG@2@W=N5;GoL};YZD`Kjo7&1_RAE zdK}X?`ZtyZIozih7Go+YvoA6PY>xA#9WAm8ZWxGJEE{3a2PiMvX^~p2jyj1&Fbixz z_{!K8sjgPgv9h$k2z1Oze?t@_<+~{>I=B4%z-SQ}E%LnkdsMH`c*U1Kr!FeV&I}+RT3O7ZZ@z7)ca#hZa|5j z-S(IDVj#$K|7wh%Hz8MMCZFiee0C#`z|Ax+(5tV;{frC^RR!!(eJqF;VW$JO>5j{= zb%4)9u_0>X82`aj4BaN}xm0MV!SV)CbZg(g1OgVxxaq9eSM{K6dO0b!r?cbcl^8G~ zrYey}(u0RQXh)Gnh5z1HM!!>14~!b1ld)_k9A&OmMZb9(wV;+8m0U{Lb9LUs4zS`x z+irg?JOhoPp4>-W!OdeF&3sw6>_q=HW5Lwy5VKJL58fZPa1Klyrrq zB7IRB7?Etr5>0g!ylMVBJo^SLh0Oz$m`bV!rv=KBH*!~(IS`orryc`Jjd-WgU4IG5 z8#Mf>-p0yg)8s`u=Tm(gznS5HrwDMpiVR2>DKbtV{G=__^}qqhp!rwp zr;Lj-37`787ZVE+ZGA&U6@h}Moz6qXK`Ex`^QRqH{o6dOnt7lUy-Fb%m~&-P*bp8r;dPNmam zVc(+{7keLNEF^m|s&VFl8N_~_6EJEt#l1WPIaihGNxjrRzdwE5IX6!WVaQ&7okL$Y zs7V?xG<;jpM$e-kpgJ2GfgwT>xT<*(H*rIGXgoTw5*=0NQV89&U^A@a(C|nSV1xnL zJP2A9+CXR-wQ94E<)zct`s&3w^vL(n<+yy?5YgYBzITil(oYEx3G6ANNcvwlPDFM| z^>B&yFcR{ITFrh5r!Cz1*Q<-sgUdLo`1`AFyB)aeQzs7<)GJJ@LwM9mUMs;kfc5q{ z^@E1W6QP!Js^?Vr5Hn_?y7+Bi#vWC?J2jFtv>1%B9bbf*G*E1NhejWuZ%{6WJB5ZC z37H{N$o|2_s{C$G+BmdD?s3zjXQxz@_xkZV_|!qmWkN3R6IB+a7)iTliT3A@h(XL- z78`}_rc|ni*wMRx&K{Vp+y6FOi~R0eLn;`8zZnW>MNzpenW!{p<1Iv^2T^@)gK^}} ze6_Aie|~hwv8r5}%~uqO)uBfiI*L=)vhVyaSYrQPd5f*3q8VSH?csF$m*XN`>_%h2 zR;lU1#Yj(z2LIWMdRpVdM*JMigjj=~*vsllP;C6lhuqDG`T)PN?#H6SHGs1`;YrHGVBR}qjJ1p*|L*oGED1OgF4hzdy{At1yAk`OpMI?wx@ z^?vX9);a6E-}kJu&R?!^mXPdw?|WbSx~~2E8P<;*(vmP={`;@uIj53Kv{lFc(FRkP zt^ESXUfz!1;z+?kJMk%V@~#Ec2v5)9_V4*Cr^ecQ3EpHeMmlpyDUsq>nyI46+jO}^ ze!JM@_8vMicGRIYYvP|@F=DLycvsP}7yR7wQLj5K8rm4;!}BNP74GQIIZ>tc7C0T*H&~61vRfQFW zrRSuvjCJem{sYHs%!-Ck|Ar51`Jd}i{g-pm8Wnt@&~IHTzW?$M^RL6hHKsMuUB3+j zAc)Btr~da?bzoTkF%zEue=`$)2)kVr9SX1^pI9sWerj|Tl0a8A-eOAOAJvNnKwlV;^POna&f_rgCj8`e-E{o*}u<(*v z`|@UW=dPmj#@^=GBob=Bw0GW``N62eqd_o2GlVVAW->>kh#nxL<3soWm>gkAg13tx z^H;YF_RjlTB2MMCd{~KuJYT$_XW)@bGFhl=ZudLucY1^zJ@`@ldSJ*DYD5@D6qwQl zFIltt4q+3Ecr@A=jY&=t^YiVEXf<-?nBCvICi9mvRKCBc61zk*-H@MhIwSVWBsFEL zJi$Wzcxg-3WUk}y833(T2Chi7&5FoJB6n7dkhQv28rdCD<%Z$#2QT%Dq&Xebhgt#? z%FoP`8OyDXS4B7cEgqW{gc=jKoSopxS8q+`?vq`eY54?BnZIq$s@mHZWwV<<-DO#8 zSKBmr9!Y1{_?^wQtd?^}dnbgWqD8h!A^1^NFy{zbF0`E#9b6pKV-nA6P4)r#fIzAJ+G2QCq*cC2@l2$JQ!@g|;BA4k z;A`?)^YQObTS@vB%99#amtQcw*=<;^1>D{%P=$R6narEy1#Qa|8ANd<7MVNUk4XLbkPb*&b3jY)i zJHk&t($&a+AR0ZN{zB?gDL^+BH#x)Ugk{2#=TzI%RR^=Vdyo-w6W_%r6r&s_S)oKEf4Mo2m`i&1xD;{5O|w{!1oXAB1$myovr=h+LN()9JOKNa`D#9 zS-B+tzeu4dno~N!@^IoLEH^jmSudz1!h%*OD6F4qI6B5}D5@jhXHp!@n+1t3Wy+mY zvVYu!J_6gf=eW0J0Y?}y7c8kpD6Ctj6DFG>$)g!j`v+es&KDl4X>pK;juEk@-tw3T z)z4As+hPlmtiaNls5p@Bt~N5y5LyIpR4pp0d-NoD)I_%^Kg6==H6K!ObVy!v(F`0S z@8i7PY@wqo|FGK96W5LoYM18{FYOCcV_7 zf+7=pQJ9a(TV!(Oy|!XJ*|<_dY^okJ4g=F+6eclq2-?OI;fy@fwVieu@8ZF^#sOi? zwlD9;Co^|#-!_Vb^HmLlRXj1M@pPTrnE|8xtUQb6PC*IhhbNpy5wm= z`@keHAw@Vc?q$~dw+hA&d!MW5s4L;I+8>CpStSL6lB?spY`shoq=hE3LvIliTqxzU z0CGi{XGH6X(10=Gas9*>@Dl76?hF5T<6;Xa^SxhJ5oCWO427f=$5X}!_W_Z zx9-E@Ven=WqNP!;5gFA{!p=7jCCb9*j<*jNaie@YO}%Hl>7qJt^-}t$YHrlUAb(R@ zT~VFTV(3*sOj4(*$t2OL2<Dn>Lu9|C5(xUXEB)_WntOhBuTFSA1#%G&2RA3WBJlG2k z@u8J`tfkcjBCXhsE(%@R`^P!n=f}#YIR_5}NZPbz)8o&D#fr3YlG?fM7p_<(EE*%A zCSl+{H{LEZ!QSA7VC2T^sqtzY*#yvDiZ$&^a?Z^*J$aJCqw;qmeW(^(dnpZZ`GIG; zaP^5YJC@cTfF~JBhLA!JE*9F^EsQLtA(9@nh0R?YgY*v8WsP-oG8|A2(O7!I`NJte zx-c#_F};fnfrhO-yp%B7*tIc7oIyF;x|u4*5Urre3AD=Ql^X%;E2EW$>l-r;uoH0e zY0>lwW&NW95O#Pqkh;Z{#7!z87|Y7O%=)L&w30Bey_ta{iSG(AiT9z=^x=&6Q*gMl zOOk(H{ltEJRj`~sk`yB&5hRk~=y4)tyCP(7P7Bz+4@FHsaiU?v7>1g@}DhaJ7 zVu_ArS%RgEa{_2#?z;c3TFJ=;dwNvI zi}wMkT>^9JYm*Ch5z;NJ2MlyMO*D9h=Mpd4k)5VXQC zt=-X>*A)w}r!^%Tc!rM6mRt+2-8pCwCi;b=5mn zzLD!j(gafaj=D1(byZF7ab(eCNJBANyxPy6kKMn62bb8a6Jr+ZXN z`YZ1G)Drbf z|ECJN2E>>qpa_>MESpn)+J)Y-5Kn9<;~Z}^?L)5dcy3S(l4r`tbIO9H&ybAJUC4>$ zdT2etW9*EVPFO0v*97-P53P7MKG|TryD7L}E+3@s5#w4C(n2aVkYW-@b&kSvZHS%A z@!IfS!!a&fv}{Pjr^h(qF0Ekecrm2v@^(WV$=&GDss(D0In(>F;$HN?<3MMmNH&Ig z)s#p(lcmWLbYjpbjong=4pA-Xl0UU=1eusifpb@B3gyhm!#q{q0yWh{mLPC!o6tV9Ivp?KE-1gsvq=~{qa_H`WxzdeYbfl8 zzjdk2>R`6|ILJmZmnv?g^2U;e1)fa|A4@$Wajt#!`RP-#W(kVl8MYLr3% zmP*!}q=j%zCTy7L3sig)!vK>n9BY}?9D`SP!m0(73LTYED~Y>!YMgC8F@l^6_84P_ z4-lxmKqu!A6!46Z&Ay_-^F zTtN-b;r3DV+j0Jf)7C{*b-x%PN25h}mJ2q7N9|l9C}7{52s|^q%oT`Ye{+JLaWT2V z+w#6Kf`v^zNIa72L6J5$!;tN{yo`BM{+PfWS*wDbqw)S8JvZ9hqWS0#0V+_$lTiKZ z_Px2k7-7@ZbF_=NDO&@Fa8cTVl6_3}gZE7~+^&eFg2_FYp=Y#jQ?L&1ENm>&LdS0@ zQw)rRa$`urUg!U_If&;}Iy4Pfz$am zlBx?3R9|Nx4nuXNg|uEdGipoWu^trH5pQsgVB;8%_C9PRIu0*dw{FNa386ZB@tzM~ zt8svKv`eeF*t$f2A2(4en2^@INO#%0v8=br5}!xoRIcB*J37 zE#4z_xJ_Nu4DndaKUVV|%epb0Af>m<^~gL$>&@BKV$XPK9SR|Mcts;7l`w(@|1sQ1 z?S#Vybi52!J*^bgre=OJQ}2X%;_-Pb zdA0Pn@;QIcjhJsE+NxY#Cs1Xcqc_ve7Rfe^1!-An$$n&;Jw=)klgmx4eG;7Pp^Mgo zZ?_+tS{7%*>v=X@Hg-X9m}k_b?^EH(eF}9L7_RF=+qc@9hBRZ35 zX%Z#NF2^P6#1)GyzK4PGplq0plzq#1W^J>krAuPrj-meHSo$y`mFhsZYHYUbNKz7* zD*FzL?+V4>V1#m^`@8-7s{o8RO{qanm6v%Gdy_IJHw=p-{&+nsw_k9jjGx;dWwKv> z^H@=ijily)z3Go!&!%87?1rnJyp^&xoZR#SgRBE+FcQ0NdsezOM-0wu|32)QO~)td zT61gRcKZvi{a#Sd;GzxhUpoj7z%a0+CVZ_&uhJw2i68{~81S&+qi&5vS7f{E&9{a3 z+Pa*KaKFTIjy)zVv)$+J<%(_151;5^WL@58ytScNQdfW3=Tv@LZ?dT7*(+Co)-VnY zYq%99-&w9*Kps!A7>(iliZ*54;@}7uE!3Sx0=aJBCCnkvx8NuCd03tdw@_~&3x!=& za{Z_q)Dr#kh`0(KDhs?En}=OOLDiow2vXz7^8-RNTqP^CJpl3(`ET)a5b0xnNP zmE%n`Cnc$S2Pv6WP-3(!9O#5WM0%Ef4kp)JtR@q_oSyfHNEmwRjI3y-2U8wb4W1;m zU%@wJUN|7J_uVlHvvP<+zU(wbLL(9Y?h}DlJUwCqE!*Y;hJzDV8_dWv^2r#$COr)? z9*J&!#_vS)^a7KSQC0}RhKrn0beowcjf)pd z(md69ad+#Y>DZ95ixa`}m#b&lZ!Zy!{bWD(b>?$p)pJZcVcapI&l0JIOIOiYb>8bu%)Q_o8|IadV~BTJm^4hDig~Hm)F-D4DX)D5}j|PrcCpEV|+A?mrxj1J$ZpBzqZ?H<3g9X z(A`Gbh^4Mw%Gm?ZKfC4JH+fpAddJPEL3F(C%5B|qHgmuoocfcqjbH9?nV1*3kd&gw zQ74`RtnfkhQkGyk?!L$7g035}EDWdXK9zJ9CwF{lDlcl!zN>#_L1q^xH}i<#lANsn zeCJJ9Yo93}Z3M0vGFPP9c0;oxE~C0_J`LouuW71(w{u@gYG>6FGMsQw_Ol~?`LDPp z`JY1rWQkMFZz=Tum^h^c6d?Z2rK11k!jS(%uhnXPH)8nr!2j3YP2DNWncw^m^^kvf zU1$N^+V8)qUjDCma)1}~kMEoNKbomnWq@dQ2vi210waJ%av?VOEz{?ds0mUoEDX@# zOi#E0qC>Cj7W=i$vWpU1ES5j8w8K!uBm2dGocb=Lali^bcXb6&iB)oVY|AxNB%SLM zbCoFrxlDub{63&m3P^spm0ZgWHvxL&`N$%!cg0t5%_aa%{th(lUt?`hS$_G0{u7PT zHwO>6TKz7g(QnEF|5L{IKa6G}0u(Nr&)7HwO#rspR|e5Hg9>1%O|!n&jXjw+c8uBK z4x|Ia)_x-=XrZm3`;2Xl!Ho^qjTFidsBkoNLOPIZ+q6q=NXT0lFSwyrn`JMSh*H6j(B5QuX@-3>gW0J?!cVz$pi> zPC2XsAbw7!^F4gX!uc5d(2+X@GQxhK40VdGEHj*1#|1#gm`HT%cp0d3oZSUIVUl}K zx1&Y0!nR}p1Q_hdH)YCK2?t}X%4dvfbSg+$H)-Ks!LE+ae*js^%I_a#4iEsxe~tCN z(Q-fxXf=BH6A|OU2h+m>W1sv_Gl(VV5c(=e@P(U5Oy=A8R;7RJ{3Qrp03bWn1<4|G zvIdZtT*@%d#{do-z$tDverYoEG%H^z`u$@&Z2|CIG@{=o zOjz-C40o@W864cM<@jp!iuR;+^jawRz^8`4-{lQZ429^9FJ+~$-Gx^Gt}RT4YX5uq z{4FIO4S((Q$_6k;%je^}WQdKgMwkd=*p0HYkYm{hfJ2)IeqA%mp2~8V+XQ7T)T7&L zV&q2VT44+8Yla*EW^c$+Is^tV^i$G@>l?q4Bz%9A0oA*+eFFz%e0;}ajetCs?aw@u zlQ3$`YJf!4~_c?;v0z2?t@x^rQgE4P!EZ?isbOC$HgRIv;Qi=>hvR zPCxZd1Z~ADL_$a?vf3+6N7Bdq({knG)&hq_jx!06*cTiV!S`K91P%K#i%dOT29FB9 zktL^*hyD!zYBvFtBm=U}$H5aCMA;h9!&1VM_yw$dY8z|1*xOP(@Xhzr$IDFwa5=)> zKTKs{Mq(k>)>M&BtA?z95!1mhf>$xXrF^{ z4*L;tb6D&f;1{}rz&0a#K&1UY zoF+@-4aN&^w*k!$V3aKcL=C*!sN!9m^TV)n2k2x6sHUv1ZwyvV&c2bDKOmKpz!ua# zla;9csOmAys}~1WxB+0`FFuGXI@=qc7VLfL#Oo?;KG}@l=XlkCI9;VxqIdWUElI+& z9s(cfH%l<>6za4*t$yLP&Ue6rYQ66^0ajFQ%Apm?m2KcqpBS7-BsG<5uVV!*>7YG_e@4GBq%SWoXm*qUxzf4uoM0E7! zL7OtA6^e+g_01NeK01Eh*r4HO%HRWBn*lS;Q{_9?xx``RqO9ktgy@GTXz4u`5+&!J zxu31P)M6dkcf2dxzxrksg}r?I%G40VXYm0Ga0VSwhNFPUc>-T}TyPPtf_i$sfnKsQ zrCu8Ej78_29dQs<0U6MxDBM&;^EM3!WqC~Wd%xRy{>;$$E<#Xf{7V=pxuS@{u(3i^ zXCSm=>88rU5{aF$dXX}h!aCrY%i zzqzB%nAv5i6^S2a7B78CK1yC)R$ej6&`35c;{5{jT+d~ate5aiy+KZHa~m`N`Jt26 zuO9#kSR-ig0~Cb}ovv!`nNyFsK1+E0@3n9#pJ4z=2SC+(UQv+$aHpVqn1lFiOZd4_ zh5c#7mGOeAj$FvPhSe5`4(=&{3@QOO1iy52!O~JeFCk} z3em&CBbC?v5~U_oT>p`)cT+#vKFXoeBD6A*89=@>z%Lk|I$XMs*0sE-!3P|)U-6CM zWkJ4I11n3iIwZA!o*bB%o2zK~FJziIzQ>tlP-C0)5__uyoVo4jd z;|mTx2L|SAaH69Ncs*(-JhG$ABK5zFuN?*axpFpPg{fN)xRq6-M11dpK8)g7NaR_2Vx$%c<8}hdT`X!|47Y`lP zio?TiWcnQzz}N)?7CN`={I>Em;+@*MUoLbh#dt?jTPLt~wVJe6K(7aFCVA+^!;Qor zpNFFWGvItMuw~qHG#`Fm*K7Ylo&K`JVe=-1?L6%KYUzhh5ANyeV6>3kHpBIXKR->fvYM!^$2CoIlNsn)5jCE1UhcG>5#ngh6K`h^Q^VR4}|=%1msLv zF|I}njUI-NT_1X~p(YjIJ6$cSagrokU`7{GQ@r*Z&;E^fgWsW; zu1cU`UiujPU$tRIKXD=o*=Xdp+_P3CbpD5psp(jy`Y$|9uo99%7*VZm2<$Nn&vY7Q zZ5Sd~{+h)yj5&OzkshLoNp7GZI92UQoc$o<7<1EUt5Mqm$=0_8iW)iLpCe6rtd%8o z(~xX2tp(+%7ZE;lN%IkXRH(Bsdj^!NOJ$+&}X zq$>|UhBvDx)YYG*C->WM%5yPzh{lp3%_nhtVcB_31aF-tlh{THPG86_S-IMnM)B}U z#}(f$Y^KnG9LE%YNRNHVyD3jtz;q%QW0u;;9F!Xb7|I2r- zo6UpnWI0-)rXFBZj3=*mwU857`lXPmF$G^HdV~K0*rUo(08Q^96C%m`^%ldHiJN;Q zm!G=mRB%m-#~qGlEQ2`96z~1K-TTy&6i?jvRqx*W{C7SUdTW53yL#jq=YgZ+o+1OC z>%LY|NMjK2jEp+;=6=?a6ddkCC$h%VX%R$iQ}$Bj^^-ULIIX;VM}boa%obQ(ZA?wh zJM~vCR{mkt2B(8O7@Fb}u!9x7(5KZWDPuVDsQRx@XSBbTEMwiq*cv+Q}bl(mr4-|NClK0Ylf%`t@B8;SS?lsXHMp z52SFMA|#jC6zSp9ga$379=V9yr#%SJWclR!63Xe#KB3{&G=tWx(l$0nn8UvxM+A)p^bb}>`^;4^U5b%D z0@#xPj1<7!*H&iLh{GnM<#Ipeg~kI_07Z5p+4H?OLVg`4JdFq5-(Y)7Jb_1yXId+d zj?X7StQ-FVR9=0&d_wO)?gcJ*3-mwJ5hqOUgM~#i5|_%gyZ9;UW{&~>d&09=o6H+L zNw_rg2>sjPB5A52iUcV(2`y1qu?hlJH< z4hAI>!n!NQ9HGL*Wz#+8S<;w4+~oH-O=Kl6zG3Zy89(mx(B?;MlAq`|lQ!yA1Qvg( z*_ECf&oHHq^rTluEC1mHcEdL$bh^kJ?6Aqw=IYar)+|;Kq@`8eFL#);nqBwBC;_Cn zp?q9ks)wHjZAkGpdpN7blE&fh>@Q2BrSxjKbKz$+3o8ZVF2an2M{>woJ*%d?xLS5# z{%PfvJ6DzzSSz&1V$Wg&wTnj&4Bu3qz|qw$xZjh$FKh28l`|8#Z__hhR4M-Tr^lQZ z|ZLMqlAERuOreXh2S` z!7J|%spM*R)pWiS>0ZL{GfCz2PQ#S@=iqjP1b@cmQ_{WU{e#T8!xly}Eo@a6N zM)Qx3CKB8f$5{oMIODeYe9*(p`;X|aFqq~I51HLUgK#ZILOVAvBCM^wd)IdB;wcC z2%D-xBHQesoM_s24pHA<6TA=h3V?iY;~G0PJ(FO%isFdI=VFJkMR;%OaqKX^fQKcY zZsoaVBn~!M!K>kWw$4_0?&8H&GXv)Vh5P+65Ii;BS;qw`f~3fMUgN)SzjoP6q(+bl z{-RJc+YL1y*1qr})cTt7cPoHZTx&-M;BDND6H&a&n z3rr)%n|XB4FcNSdsl#PGh*j_6zH zRUqaSo?g!Let?cYre^kM+$KlsiN-h2Q-})%Re^8P0+sc)s{3yI+w{NJC)R4 z9-%dP-NOYPtm2$*Y4p$`5m@}9Nn4oXhoe##`0wgUg4(n)R&J|jKNmhQAkzj{w@r;u zgFweR$Uzv|fhdg>Det!#WJ-^mgp{lD$$+P8K$c8BX#S^}CNOH0p+$=YVDfkSeBoYD z^v|pZc$qxqo0ld+{*#afYp|=${l$J4G|CS|V;?7gXzNYD3Fn0A(<+M62tO~*fs=0Q zU)VY5X}lCqTU(BqeZIc+s53;m+(nmpX*!5X>U-uNF+LwQ3fs~~-Y_D!SN{7kzthFd zH#5HtuMiHCzoT$)%Ex>D2)wuLC@kMScDj`uHhrt{XrJ2OZ_t zlL7%$SU46;X1(18z}b+ZB=H{Z%2CD%pzndLE}}_*MdwsL(hE<);Y6__@~@M9GB(v7 zk|l5+=BIiG6OS{5O+c&QIfnD$Y*l+{QXN%8-Td5Qb;04p*4URDfJe)6`PC96w#o2v zbv+@wY?Ypi$`tWt0xTVlHPfTrbji8?Z8wVtbTSe@HCSD@*Es-r=mNW!BFFQ+>~ulD zdVzO#b3v!>#%j3IR*RrLW6V?&{E_RB5z=NhOd3Ykz|XRcn6zZH>D8IH@7sO?&d+rF+dw#C@KcPDa9(9#e( z=JNV4ZyhnKo|j_d0ma-SL4R}F= z^)I&@oR)dh9p^Il1JbmP=HouRkqNnel3%s3PBU30d1ayWn`H)|*l)2a9TLH1YOU)7 z=#Hp_e>i4_&R6$RLJre09BWIlVaG!o66qfd5h-Z5( z9Im+#2kR!A;%WpBUKw0>^AIM&i<(lWdYSJ8i+$PJoK% z)DMJj9;h<0tqs+U=;Mhi>(idWD_>oSr$0lE*EM?DmBO;fu- zEK8g00G?jMn3gx+0khl?q{3op&%UpgF**0GN1P$;;J}Cgr^1_5jbxoQA~U05)};W34Lm3DRZpr!tk+B&}6HEs4%~)-uWgi$XLQLx#g)ldEK^le($?8BN(FE zq26$|La#&Omp9De@{~K&Jv^h*Tbzi@dh#}3wNzWLkd4_0tWu-(>)}Q9x8lL$CAyIc zKhm2hBmufH=j%g>jvuP}2-!)i%S?sVb1!rTkS{!9s6o z)*|PM0d1Z>zSOa#34AoIJj2rXBslo?uZBV5He$=jt<+?#ER$(@$B2?A3-8nqoHX5C zd6IMpdi@k_SvbaN+LjK_K2KS6#W_&eL}6sl$K~T^+7*uznR<=klOL^UHD4cyaA6Wd ztC5A5!U)h)&wyOmiL-t$*^zye(E{^nsH*>WWj!OpWV7%HoE`8)EJ)8c~ zSw-@0n)*vAXjB@S04V6^ehM<-+qInYmu{q|Ug%|@8i9L;5hSZNq~=zG``}L45JdUzb$t7t|$%+li5NdU*Y!lAGMO01Is3;r%JN17>dZC4 zq@0`-JWMQ(1{d6at|ObzqcUW=L~G-TTFAR5(UBC06!`qM?v?Ik=A5*8H+6Udmlvah z@G4n-DA|ktd3N#re2s#Z+S|Q^!mEkT*a!TAlb{xW=N#a+VwtcH9QY8S^CsnNx7xLM*t}Tn zExl`fUE>mIpcJT5?}dvaoT8R@oft@aOB_IYSGeDc2^!9L8w=Beq9BEgD3TG z@ex|Ov9JCue-~kyVMTP!TN`QaC<92G`PR!x!@EAb5C11guB{u>nDxfJ--9>{i%r4)jw< z@HR-h@z_Fx0V+uIB9Hlr3>sM z^?DmuVvkt+U*GFa>(T{jg}hVQHebcQKwm1QhUn)St>>nJ;aj9EhSYOm1c}1y znvzSz&vMPEWI9GUBa$>0mnUqX!3j zzkWC#!>MWL4+5DYfm-y?Wy&xnS<01_0=3DxY%l>)GWYy->b8>Qx3X$UWS(coitS|b z+LFYIT>;?)5Vg1YBD{-hb40-DYI$N!j9K$6u2n`E-K$vwZj(|LsBL{6;v{e^0p^&@ z`vC=7&#4+rsr+ZId&-9v&b27VsB5*=OB>$KSHMFWL^Q^9qLhGCM!zU-3OTNoVqo|^S^#f z$qUW3xF~*IxAfqbFU_v~iXpxY;DA1t7F<7i3wW4!Y3* z=tyV%$#zu29QSQW7|URMVygfLkgDt-j})ob+Ot{n*A<+MS~y#L6LV?Qg! zez-1^t>N?x6dUB+381`OPz_{J?L^rp+Xpt8^*|ElZQJtLm|N5EwN`7lnfP>_W@+6) z^mgFu%q0PBwbM7zOcb~qg956o_ib}X40n^yt!bc+DT=-Hh3M5gV(>&8Z6=FF!(a3BsO68hjT~ZMZ~ME^ z;P(HhT|qXGFCYC^T#fwKUGn^ozu9ms0`g4|^G5Ak%h9|4xx1QxYW?>=y!pTO5C8uk dKi=BLZAxVNi@Sym;0dm?IeY$0-6`K2{|$FxZp8oq literal 0 HcmV?d00001 diff --git a/src/data/tr/articles.json b/src/data/tr/articles.json index 8743e99..0badc07 100644 --- a/src/data/tr/articles.json +++ b/src/data/tr/articles.json @@ -1,4 +1,9 @@ [ + { + "title": "Linear Model Nedir?", + "slug": "linear-model-nedir", + "category": "machine-learning" + }, { "title": "Linear Regression Nedir?", "slug": "linear-regression-nedir",