AI-konst och generativ konst, egentränade neurala nätverk, Nadeschda Barenje

YouAndMeNet är en egentränad diffusionsmodell byggd från grunden på egna fotografier. Generativ konst med GAN och neurala nätverk, Python och PyTorch, utan förtränade vikter eller kommersiella AI-tjänster. Stockholm, Konstfack 2026.

YouAndMeNet is a self-trained conditional diffusion model built from scratch on personal photographs. Generative art using GAN and neural networks, Python and PyTorch, no pretrained weights. Stockholm, Konstfack 2026.

YouAndMeNet - Diffusion v2 Technical Documentation X

YouAndMeNet

Villkorlig diffusionsmodell för generering av konstobjektConditional Diffusion Model for Art Object Generation

DIFFUSION v2 ACTIVE
Vad är diffusion? - Teknisk bakgrundWhat is Diffusion? - Technical Background

GrundidénThe Core Idea

Diffusionsmodeller lär sig att vända en gradvis brusningsprocess. Istället för att direkt generera bilder (som GANs) lär de sig att avbrusa bilder steg för steg.Diffusion models learn to reverse a gradual noising process. Instead of learning to generate images directly (like GANs), they learn to denoise images step by step.

Framåtprocessen (lägger till brus)Forward Process (Adding Noise)

Vi lägger gradvis till gaussiskt brus på en bild över T tidssteg tills den blir rent brus:We gradually add Gaussian noise to an image over T timesteps until it becomes pure noise:

x0x1x2 → ... → xT (rent brus)(pure noise)

Vid varje steg:At each step: xt = √αt · x0 + √(1-αt) · ε
där ε ~ N(0, I) är slumpmässigt gaussiskt bruswhere ε ~ N(0, I) is random Gaussian noise

Bakåtprocessen (avbrusning)Reverse Process (Denoising)

Det neurala nätverket lär sig att förutsäga det brus som lades till, vilket gör att vi kan vända processen:The neural network learns to predict the noise that was added, allowing us to reverse the process:

xT (brus)(noise)xT-1 → ... → x0 (bild)(image)

Modellen förutsäger:Model predicts: εθ(xt, t, c)brus lagt till vid steg tnoise added at step t
Givet villkor c (våra 32 features)Given condition c (our 32 features)

Varför det fungerarWhy This Works

  • Enklare inlärning: Att förutsäga brus är lättare än att förutsäga hela bilderSimpler learning: Predicting noise is easier than predicting entire images
  • Stabil träning: Enkel MSE-förlust, inget adversariellt min-max-spelStable training: Simple MSE loss, no adversarial min-max game
  • Hög kvalitet: Många små avbrusningssteg = finkornad kontrollHigh quality: Many small denoising steps = fine-grained control
  • Villkorlig generering: Injicera bara villkoret i varje avbrusningsstegConditional generation: Just inject condition into each denoising step
ModellöversiktModel Overview
~48M
PARAMETERS
32
FEATURES
256×256
RESOLUTION
1000
TIMESTEPS
50
DDIM STEPS

Vad den gör:What it does:

  • Tar 32 feature-värden som beskriver färg, ljussättning, material, form, topologiTakes 32 feature values describing color, lighting, material, shape, topology
  • Genererar 256×256-bilder som matchar dessa featuresGenerates 256×256 images that match those features
  • Tränad ENBART på mina egna foton (~3000 bilder från kamerarullen, glasverk)Trained ONLY on my own photos (~3000 images from camera roll, glasswork)
  • Inga förtränade vikter - tränad från grundenNo pretrained weights - trained from scratch
  • Ingen extern data - bara mina fotografierNo external data - only my photographs
Koddjupdykning - hovra för förklaringarCode Deep Dive - Hover for Explanations MY CODE

1. ResBlock - ByggblocketThe Building Block

Varje nivå i U-Netet använder ResBlocks. De bearbetar features och injicerar tid+villkorsinformation:Every level of the U-Net uses ResBlocks. They process features while injecting time+condition information:

class ResBlock(nn.Module): """x → GN → SiLU → Conv → (+emb) → GN → SiLU → Dropout → Conv → (+skip)""" def __init__(self, in_ch, out_ch, emb_dim, dropout=0.1): self.conv1 = nn.Sequential( nn.GroupNorm(32, in_ch), nn.SiLU(), nn.Conv2d(in_ch, out_ch, 3, padding=1) ) self.emb_proj = nn.Sequential( nn.SiLU(), nn.Linear(emb_dim, out_ch) ) self.conv2 = nn.Sequential( nn.GroupNorm(32, out_ch), nn.SiLU(), nn.Dropout(dropout), nn.Conv2d(out_ch, out_ch, 3, padding=1) ) # Skip connection: 1x1 conv if channels change, otherwise identity self.skip = nn.Conv2d(in_ch, out_ch, 1) if in_ch != out_ch else nn.Identity() def forward(self, x, emb): h = self.conv1(x) # [B, out_ch, H, W] h = h + self.emb_proj(emb)[:, :, None, None] # Inject embedding! h = self.conv2(h) # [B, out_ch, H, W] return h + self.skip(x) # Residual connection

2. Sinusoidal Time Embedding FROM PAPER

Vi kodar tidsteget t (0-999) till en 64-dimensionell vektor med sinus/cosinus-funktioner vid olika frekvenser:We encode the timestep t (0-999) into a 64-dimensional vector using sine/cosine functions at different frequencies:

class SinusoidalEmbedding(nn.Module): """t (int) → embedding (64,)""" def forward(self, t): half_dim = self.dim // 2 # 32 emb = math.log(10000) / (half_dim - 1) # ~0.29 emb = torch.exp(torch.arange(half_dim) * -emb) # [32] frequencies emb = t[:, None] * emb[None, :] # [B, 32] - t × each freq return torch.cat([emb.sin(), emb.cos()], dim=-1) # [B, 64]

Varför det fungerar: Olika tidssteg behöver olika information. Tidiga steg (t≈0) behöver fina detaljer, sena steg (t≈999) behöver global struktur. De varierande frekvenserna kodar båda.Why this works: Different timesteps need different information. Early steps (t≈0) need fine details, late steps (t≈999) need global structure. The varying frequencies encode both.

3. Self-Attention MY CODE

Används vid 32×32 och 16×16 upplösningar. Låter modellen titta på alla rumsliga positioner när den genererar varje pixel:Applied at 32×32 and 16×16 resolutions. Lets the model look at all spatial positions when generating each pixel:

class SelfAttention(nn.Module): def __init__(self, channels, num_heads=4): self.head_dim = channels // num_heads self.qkv = nn.Conv2d(channels, channels * 3, 1) self.proj = nn.Conv2d(channels, channels, 1) def forward(self, x): B, C, H, W = x.shape residual = x # Compute Q, K, V qkv = self.qkv(self.norm(x)).reshape(B, 3, self.num_heads, self.head_dim, H*W) q, k, v = qkv[:, 0], qkv[:, 1], qkv[:, 2] # Each [B, heads, head_dim, H*W] # Scaled dot-product attention attn = torch.matmul(q.transpose(-2,-1), k) * (self.head_dim ** -0.5) attn = F.softmax(attn, dim=-1) # [B, heads, H*W, H*W] # Apply attention to values out = torch.matmul(attn, v.transpose(-2,-1)) # [B, heads, H*W, head_dim] out = out.reshape(B, C, H, W) # Back to spatial return self.proj(out) + residual

4. DDIM Sampling MY IMPLEMENTATION

DDIM (Denoising Diffusion Implicit Models) möjliggör snabb sampling på 50 steg istället för 1000:DDIM (Denoising Diffusion Implicit Models) allows fast sampling in 50 steps instead of 1000:

def ddim_sample(self, cond, ddim_steps=50, eta=0.0): # Start with pure noise x = torch.randn((batch_size, 3, 256, 256)) # Take larger steps: 1000/50 = every 20th timestep timesteps = list(range(0, 1000, 1000//ddim_steps))[::-1] for t in timesteps: # Model predicts the noise that was added pred_noise = self.model(x, t, cond) # DDIM formula to predict original image alpha_t = self.alphas_cumprod[t] pred_x0 = (x - sqrt(1 - alpha_t) * pred_noise) / sqrt(alpha_t) pred_x0 = clamp(pred_x0, -1, 1) # Move to next (less noisy) timestep alpha_prev = self.alphas_cumprod[t_prev] x = sqrt(alpha_prev) * pred_x0 + sqrt(1 - alpha_prev) * pred_noise return x
Architecture Diagram 100% MY CODE
YouAndMeNet - U-Net Architecture (~48M parameters) ════════════════════════════════════════════════════════════════════════════ INPUTS: x [B, 4, 256, 256] Noisy image (3 RGB) + mask (1 channel) t [B] Timestep (0-999) cond [B, 32] Conditioning features ┌───────────────────────────────────────┐ │ EMBEDDING PIPELINE │ │ │ │ t ──→ SinusoidalEmb(64) ──→ MLP ──┐ │ │ │ │ │ cond ──→ MLP(32→256) ─────────────┼──┼──→ emb [B, 256] │ │ │ │ (add)┘ │ └───────────────────────────────────────┘ │ │ emb injected at every ResBlock ↓ ┌───────────────────────────────────────────────────────────────────────────┐ │ ENCODER │ ├───────────────────────────────────────────────────────────────────────────┤ │ │ │ Level 0: 256×256 64 ch [ResBlock ×2] ────────┐ │ │ ↓ Downsample (stride 2 conv) │ │ │ │ │ │ Level 1: 128×128 128 ch [ResBlock ×2] ──────┐ │ │ │ ↓ Downsample │ │ │ │ │ │ │ │ Level 2: 64×64 256 ch [ResBlock ×2] ────┐ │ │ │ │ ↓ Downsample │ │ │ │ │ │ │ │ │ │ Level 3: 32×32 512 ch [ResBlock ×2] + [SelfAttention] ──┐ │ │ │ │ │ ↓ Downsample │ │ │ │ │ │ │ │ │ │ │ │ Level 4: 16×16 512 ch [ResBlock ×2] + [SelfAttention] │ │ │ │ │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────┼──┼─┼─┼──┘ │ │ │ │ │ ┌───────────────────┼─────────────────────────┼──┼─┼─┼──┐ │ BOTTLENECK (8×8, 512 ch) │ │ │ │ │ │ │ │ │ │ │ │ [ResBlock] + [SelfAttention] + │ │ │ │ │ │ [ResBlock] │ │ │ │ │ │ │ │ │ │ │ └───────────────────┼─────────────────────────┼──┼─┼─┼──┘ │ │ │ │ │ ┌───────────────────────────────────────┼─────────────────────────┼──┼─┼─┼──┐ │ DECODER │ │ │ │ │ ├───────────────────────────────────────────────────────────────────────────┤ │ │ │ │ │ │ │ Level 4: 16×16 ← concat skip ──────────────────────────────┘ │ │ │ │ │ [ResBlock ×2] + [SelfAttention] │ │ │ │ │ ↑ Upsample (nearest + conv) │ │ │ │ │ │ │ │ │ │ Level 3: 32×32 ← concat skip ─────────────────────────────────┘ │ │ │ │ [ResBlock ×2] + [SelfAttention] │ │ │ │ ↑ Upsample │ │ │ │ │ │ │ │ Level 2: 64×64 ← concat skip ───────────────────────────────────┘ │ │ │ [ResBlock ×2] │ │ │ ↑ Upsample │ │ │ │ │ │ Level 1: 128×128 ← concat skip ─────────────────────────────────────┘ │ │ [ResBlock ×2] │ │ ↑ Upsample │ │ │ │ Level 0: 256×256 ← concat skip ────────────────────────────────────────┘ │ [ResBlock ×2] │ │ │ └───────────────────────────────────────────────────────────────────────────┘ │ ┌───────────────────┼───────────────────┐ │ FINAL LAYER │ │ │ │ [GroupNorm] → [SiLU] → [Conv 3×3] │ │ │ └───────────────────┼───────────────────┘ │ ↓ OUTPUT: [B, 3, 256, 256] (predicted noise ε)
Varför icke-semantiska features?Why Non-Semantic Features? MY DESIGN DECISION

Problemet med semantiska beskrivningarThe Problem with Semantic Descriptions

De flesta moderna bild-AI:er använder textpromptar: "ett foto av en blå glasvas i mjukt ljus". Men textbeskrivningar är:Most modern image AI uses text prompts: "a photo of a blue glass vase in soft lighting". But text descriptions are:

  • Subjektiva - "mjukt ljus" betyder olika saker för olika personerSubjective - "soft lighting" means different things to different people
  • Ofullständiga - man kan inte beskriva varje visuell detalj med ordIncomplete - you can't describe every visual detail in words
  • Förlustbringande - information går förlorad när man omvandlar visuellt till språkLossy - information is lost when converting visuals to language
  • Partiska - tränade på internettexter med sina kulturella antagandenBiased - trained on internet captions with their cultural assumptions

Min metod: numeriska bildtokenMy Approach: Numerical Image Tokens

Idén kom från att jag försökte bakåtkonstruera bilder: tänk om man istället för att beskriva en bild med ord kunde beskriva den med siffror som fångar visuella egenskaper direkt?I got the idea by trying to reverse-engineer images: what if instead of describing an image with words, I could describe it with numbers that capture visual properties directly?

Traditionell textkonditionering:Traditional Text Conditioning:
"blue vase" → CLIP encoder → [0.2, -0.1, 0.8, ...] (768-dim semantic embedding)

Min icke-semantiska konditionering:My Non-Semantic Conditioning:
Image → Feature extractors → [hue=0.6, sat=0.7, circularity=0.4, ...] (32-dim visual properties)

Det är som att bygga ett tokenordförråd för bilder - men istället för diskreta ordtoken använder jag kontinuerliga visuella mätningar. Varje feature är:This is like building a token vocabulary for images - but instead of discrete word tokens, I use continuous visual measurements. Each feature is:

  • Objektiv - beräknad direkt från pixlar, inte tolkadObjective - computed directly from pixels, not interpreted
  • Fullständig - fångar aspekter som ord inte kan uttryckaComplete - captures aspects words can't express
  • Invertibel - samma features kan återskapa samma visuella stilInvertible - the same features can reconstruct the same visual style
  • Språkoberoende - fungerar utan textträningsdataLanguage-independent - works without any text training data

Varför 32 features?Why 32 Features?

Jag experimenterade med olika feature-antal (16, 32, 64, 128). 32 features ger en bra balans:I experimented with different feature counts (16, 32, 64, 128). 32 features provide a good balance:

  • Tillräckligt många dimensioner för att fånga distinkta visuella egenskaperEnough dimensions to capture distinct visual properties
  • Tillräckligt få för att modellen ska kunna lära sig deras innebördSmall enough that the model can learn their meaning
  • Varje feature är tolkningsbar och kontrollerbarEach feature is interpretable and controllable

Tänk på det som ett 32-dimensionellt visuellt rum där varje axel representerar en mätbar egenskap.Think of it as a 32-dimensional visual space where each axis represents a measurable property.

32 Conditioning Features MY EXTRACTION CODE

Varje bild i datamängden har 32 features extraherade. Under generering specificerar du dessa features för att styra resultatet:Each image in the dataset has 32 features extracted. During generation, you specify these features to control the output:

FÄRGCOLOR (8)

  • dominant_hue - Huvudfärg (0-1, mappar till 0-360°)Main color (0-1, maps to 0-360°)
  • dominant_sat - Mättnad på huvudfärgenSaturation of main color
  • dominant_val - Ljusstyrka på huvudfärgenBrightness of main color
  • color_variety - Hur många distinkta färgerHow many distinct colors
  • temperature - Varmt (rött/gult) vs svalt (blått)Warm (red/yellow) vs cool (blue)
  • sat_variation - MättnadsomfångSaturation range
  • color_contrast - Skillnad mellan färgerDifference between colors
  • high_sat_ratio - % levande färger% of vivid colors

LJUSSÄTTNINGLIGHTING (6)

  • specular_highlights - Blanka punkterShiny spots
  • shadow_intensity - Hur mörka skuggorna ärHow dark shadows are
  • shadow_ratio - % av bilden i skugga% of image in shadow
  • brightness_var - LjusvariationLight variation
  • local_contrast - KantskärpaEdge sharpness
  • obj_bg_contrast - Objekt vs bakgrundObject vs background

MATERIALMATERIAL (5)

  • reflectivity - Spegellik kvalitetMirror-like quality
  • transparency - GenomskinlighetSee-through quality
  • roughness - Texturens grovhetTexture graininess
  • metallic - Metalliskt utseendeMetal-like appearance
  • color_uniformity - Enhetlig vs varieradSolid vs varied

FORMSHAPE (7)

  • circularity - Hur rund (0-1)How round (0-1)
  • solidity - Fylld vs ihåligFilled vs hollow
  • spectral_entropy - Frekvensmässig komplexitetFrequency complexity
  • graph_density - KantanslutningEdge connectivity
  • mean_curvature - Genomsnittlig krökningAverage bendiness
  • curv_variance - KröknadsvariationCurvature variation
  • affine_curvature - Skalinvariant kurvaScale-invariant curve

TOPOLOGITOPOLOGY (6)

  • euler_char - V - E + F formelnformula
  • genus - Antal "handtag"Number of "handles"
  • num_holes - Hål i objektetHoles in object
  • complexity - Övergripande intrikathetOverall intricacy
  • spectral_gap - Graffens egenvärdesgapGraph eigenvalue gap
  • betti_1 - 1D topologinummer1D topology number
TräningsprogressionsproverTraining Progression Samples
Transparens: hur projektet gjordesTransparency: How This Was Made

BakgrundBackground

Det här projektet utvecklades över flera månader medan jag studerade programmering på yrkesskola (1,5 år). Koden skrevs i dialog med AI-assistans (Claude) - jag skrev kod, diskuterade problem, förstod lösningar och implementerade dem på mitt eget sätt.This project was developed over several months while studying programming at vocational school (1.5 years). The code was written in dialogue with AI assistance (Claude) - I would write code, discuss problems, understand solutions, and then implement them in my own way.

Vad jag skapadeWhat I Created

  • Kärnkonceptet: Att använda icke-semantiska numeriska features istället för textpromptar - min egen idé från försöken att "bakåtkonstruera" hur man beskriver bilder utan ordThe core concept: Using non-semantic numerical features instead of text prompts - my own idea from trying to "reverse-engineer" how to describe images without words
  • Arkitekturbeslut: Kanalprogression (64→128→256→512→512), attention-placering vid 32×32 och 16×16, antal ResBlocks per nivåArchitecture decisions: Channel progression (64→128→256→512→512), attention placement at 32×32 and 16×16, number of ResBlocks per level
  • 32 feature-systemet: Att välja vilka visuella egenskaper som spelar roll (färgtemperatur, topologi, krökning etc.) och hur man normaliserar demThe 32 feature system: Selecting which visual properties matter (color temperature, topology, curvature, etc.) and how to normalize them
  • Projektstruktur: Organisera filer, namnkonventioner, dela upp koden i modulerProject structure: Organizing files, naming conventions, splitting code into modules
  • Alla experiment: 18 misslyckade GAN-versioner innan jag bytte till diffusion, var och en med olika ansatserAll experiments: 18 failed GAN versions before switching to diffusion, each with different approaches
  • Datamängd: ~7000+ egna fotografier, manuellt kureradeDataset: ~7000+ of my own photographs, manually curated

Exempel på kod jag skrevExamples of Code I Wrote

VadWhatExempel på mina beslutExample of my decisions
TräningsparametrarTraining parametersValde batch_size=4, lr=1e-4, 1000 tidssteg, cosine scheduleChoosing batch_size=4, lr=1e-4, 1000 timesteps, cosine schedule
ModellkonfigurationModel configbase_channels=64, channel_mult=[1,2,4,8,8], num_res_blocks=2
Feature-urvalFeature selectionBeslutade att använda Euler-karakteristik och Betti-tal för topologiDeciding to use Euler characteristic and Betti numbers for topology
FilorganisationFile organizationDelade upp i diffusion_model.py, train_diffusion.py, build_features.pySplitting into diffusion_model.py, train_diffusion.py, build_features.py
Loggning och checkpointsLogging & checkpointsSparar prover var N:e epok, tensorboard-integrationSaving samples every N epochs, tensorboard integration
DataaugmenteringData augmentationValde vilka transformationer som ska tillämpas (vändningar, color jitter etc.)Choosing which transforms to apply (flips, color jitter, etc.)
FelsökningDebuggingHittade varför träningen divergerade, fixade NaN-förluster, minnesoptimeringFinding why training diverged, fixing NaN losses, memory optimization

Vad AI hjälpte medWhat AI Helped With

Jag använde både ChatGPT och Claude Code under projektets gång. De gav olika perspektiv och styrkor, vilket hjälpte mig förstå problem ur flera vinklar.I used both ChatGPT and Claude Code throughout the project. They brought different perspectives and strengths, which helped me understand problems from multiple angles.

OmrådeAreaTyp av hjälpType of help
Enformiga beräkningarMonotonous calculationsSkriva repetitiva kodmönster, standardkod, tensoromformningsoperationerWriting repetitive code patterns, boilerplate, tensor reshaping operations
Matematisk implementationMath implementationOmvandla papperekvationer till PyTorch (alpha schedules, brussampling, förlustfunktioner)Translating paper equations into PyTorch (alpha schedules, noise sampling, loss functions)
Konkreta exempelConcrete examplesVisa hur ett koncept ser ut i faktisk kod, inte bara teoriShowing how a concept looks in actual code, not just theory
Implementera principerImplementing principlesOmvandla designidéer ("jag vill ha attention här") till fungerande kodTurning design ideas ("I want attention here") into working code
Hitta länkar och referenserFinding links & referencesLokalisera relevanta papers, dokumentation och handledningarLocating relevant papers, documentation, and tutorials
FelsökningDebuggingFörklara felmeddelanden, hitta tensorformatmissmatchningar, minnesproblemExplaining error messages, finding tensor shape mismatches, memory issues
BiblioteksanvändningLibrary usageKorrekt syntax för OpenCV, scikit-image, PyTorch, torchvision-funktionerCorrect syntax for OpenCV, scikit-image, PyTorch, torchvision functions
KonceptConceptsFörklara attention-mekanismer, normaliseringstekniker, förlustlandskapExplaining attention mechanisms, normalization techniques, loss landscapes
KodstrukturCode structureFöreslå hur man organiserar klasser, delar filer, skriver docstringsSuggesting how to organize classes, split files, write docstrings
Olika perspektivDifferent perspectivesChatGPT och Claude föreslog ofta olika ansatser - att jämföra dem hjälpte mig lära migChatGPT and Claude often suggested different approaches - comparing them helped me learn

Bibliotek som användesLibraries Used

  • PyTorch - Ramverk för neurala nätverkNeural network framework
  • torchvision - Bildtransformationer och verktygImage transforms and utilities
  • OpenCV - DatorsynsoperationerComputer vision operations
  • scikit-image - BildbehandlingsalgoritmerImage processing algorithms
  • NumPy - Numerisk beräkningNumerical computing
  • tqdm - FörloppsindikatorerProgress bars
  • TensorBoard - TräningsvisualiseringTraining visualization

Papers som refereradesPapers Referenced

  • DDPM - Ho, Jain, Abbeel: "Denoising Diffusion Probabilistic Models" (NeurIPS 2020)
  • DDIM - Song, Meng, Ermon: "Denoising Diffusion Implicit Models" (ICLR 2021)
  • Improved DDPM - Nichol, Dhariwal: "Improved Denoising Diffusion Probabilistic Models" (2021)
  • U-Net - Ronneberger et al.: "U-Net: Convolutional Networks for Biomedical Image Segmentation" (2015)
  • Attention - Vaswani et al.: "Attention Is All You Need" (NeurIPS 2017)
  • Group Norm - Wu, He: "Group Normalization" (ECCV 2018)
SAMMANFATTNINGIN SUMMARY
Det här projektet utvecklades i dialog med AI och kombinerar mina 1,5 år av programmeringsstudier med AI-assistans för implementeringsdetaljer. Idéerna, designbesluten, experimenten och datamängden är mitt eget arbete. Inga förtränade vikter användes - modellen tränade från grunden på ~7000 av mina egna foton.This project was developed in dialogue with AI, combining my 1.5 years of programming studies with AI assistance for implementation details. The ideas, design decisions, experiments, and dataset are my own work. No pretrained weights were used - the model was trained from scratch on ~7000 of my own photos.