Chapter 1: The Spark: A Simple Idea That Changes Everything
The cursor on Mia’s laptop blinked at 2:17 a.m., mocking the half-edited bug fix staring back at her. The apartment was silent except for the faint, persistent whine coming from the living room, where her three-year-old tabby Mochi was alternately pawing at the empty ceramic food bowl and scratching the leg of the sofa. She’d been so deep in squashing a race condition in the company’s new payment service that she’d completely forgotten to refill Mochi’s kibble after her evening feed at 8 p.m. The vet bill from three months prior, when a similar 72-hour forgetfulness episode had left Mochi vomiting stomach acid and requiring a $400 hydration drip, still stung on her credit card statement. She’d promised herself she’d never let that happen again. And yet, here they were.
She dropped her laptop and padded into the living room, scooping Mochi up into her arms. The cat’s fur was matted in a few places from where she’d been scratching at the sofa leg, and her ears, one of which bore a small, scarred notch from a stray dog attack before Mia had adopted her from the shelter, twitched with irritation. “I’m sorry, baby,” Mia mumbled into her fur, pressing a kiss to the scarred ear. “I’ll make it up to you. No more late nights forgetting your dinner. I promise.”
The idea hit her before she could set Mochi down: what if she built an app that would never let her forget? She’d seen those smart pet feeders online, but they were expensive, and Mochi hated the automated ones anyway—she’d refuse to eat from them for days after Mia first tried one last year. What if she built something simpler? A mobile app that used her phone’s camera to watch the food bowl, recognize when Mochi was there to eat, and send her a push notification if Mochi hadn’t shown up by her usual 7 p.m. feeding time. Even better: if the app could recognize when Mochi was eating, it could even log how much she ate, so Mia could track her weight after her recent vet visit.
Mia was a junior full-stack developer at a small seed-stage startup, fresh off a computer science degree and six months of building backend services and React frontends for the company’s SaaS product. She’d taken a single introductory deep learning course on Coursera the year before, where she’d learned to build a cat/dog classifier that hit 95% accuracy on a small public dataset in a single afternoon. She’d never built anything “real” with AI before, but this problem felt small enough, personal enough, that she was sure she could pull it off. She’d build the app, never forget to feed Mochi again, and maybe even turn it into a side project she could put on her resume. She spent the rest of the night sketching out the core workflow on a sticky note, then passed out on the couch with Mochi curled on her chest, the empty food bowl sitting on the coffee table between them.
By the next evening, Mia had already pulled down a PyTorch tutorial for image classification and downloaded the ImageNet-pretrained ResNet18 model from TorchVision, the official PyTorch computer vision library. She’d chosen ResNet18 for two reasons: first, the Coursera course had used it as an example of a powerful pre-trained model, and second, a quick Google search told her it was small enough to run on mobile devices. She didn’t know the first thing about model architecture design, but she knew enough to understand the core idea behind transfer learning: a model that had already been trained on 1.2 million images across 1000 common object categories (the ImageNet dataset) had already learned to recognize edges, textures, shapes, and basic visual features that applied to almost any image classification task. All she had to do was replace the final layer of the ResNet18, which originally output probabilities for 1000 classes, with a new layer that output probabilities for just two classes: “cat” and “not cat”. Then she could fine-tune the model on her own small dataset of cat photos, and it would work for her use case. It felt almost too easy.
The first hurdle was data. The tutorial she was following used a clean, curated public dataset of cat and dog photos, but Mia didn’t have that. She spent three nights after work scraping images: she pulled 4,000 photos of cats from Google Images, Baidu Images, and the Instagram #cat hashtag, adding 1,000 more photos she’d taken of Mochi over the years—close-ups, side profiles, blurry shots of her mid-leap after a toy, even a few photos of Mochi curled up asleep on a fuzzy blanket that looked almost like a cat from a distance. For negative samples, she scraped 2,000 photos of dogs, rabbits, hamsters, potted plants, couch cushions, and the same fuzzy blanket Mochi liked to sleep on. She spent an entire Sunday afternoon labeling every single image with LabelImg, the open-source annotation tool she’d downloaded, double-checking her work three times to make sure she hadn’t mislabeled any dogs as cats or vice versa. By the end of it, her eyes were strained, and she’d drunk enough iced coffee to power a small apartment for a week, but she had a clean, labeled dataset of 7,300 images split 70/20/10 into training, validation, and test sets.
She wrote the training script in a Jupyter notebook on her M1 MacBook Pro, following the tutorial’s code almost exactly: load the pre-trained ResNet18, swap out the final fully connected layer, set the loss function to cross-entropy, the optimizer to Adam with a learning rate of 1e-4, and train for 10 epochs with a batch size of 32. The whole training process took less than 20 minutes. When the final epoch finished, she leaned forward, squinting at the output in the notebook cell:
> Epoch 10/10, Loss: 0.0821, Val Acc: 0.9502
95% accuracy. She whooped so loud Mochi, who’d been curled up on the windowsill watching the rain, jumped and knocked over a potted succulent. Mia didn’t even care. She ran a quick test on a handful of random images from the test set: a photo of Mochi mid-sneeze, a blurry shot of a cat in a dimly lit alley, even a photoshopped image of her own face pasted onto a cat’s body—all were classified correctly as “cat”. She tested a few negative samples too: a photo of a golden retriever, a shot of her fuzzy blanket, a picture of a stuffed animal cat—all were correctly classified as “not cat”. It worked. It actually worked.
She spent the next two days building a barebones Android app in Android Studio: a simple interface with a camera preview, a button to trigger a photo capture, and a text view to display the model’s output. She followed the official PyTorch Mobile tutorial to convert her trained ResNet18 model to ONNX (Open Neural Network Exchange) format, a universal format for AI models that could be run on almost any device, then integrated the ONNX Runtime for Android into her app. The tutorial promised that deployment was “seamless” for models under 100MB, and her ResNet18 was only 44MB in ONNX format—surely a 3GB RAM phone could handle that. She compiled the APK, installed it on her old 2019 Redmi phone, and tapped the app icon with a grin.
The app opened, the camera preview flickered on for half a second, and then the screen went black. A pop-up appeared: *“App has stopped.”*
Mia stared at her phone, confused. She opened the logcat output in Android Studio, scrolling through the wall of debug text until she found the error: ``` java.lang.OutOfMemoryError: Failed to allocate a 786432000 byte allocation with 419430400 free bytes and 394MB down until OOM ``` Out of memory. The model was trying to allocate 750
Mia stared at the error message on her laptop screen, the red text blurring a little as the exhaustion from three nights of data scraping caught up with her. 750 megabytes of allocation failed? The ResNet18 model file was only 44 megabytes—she’d checked the file size three times after converting it to ONNX format, smugly noting how small it was compared to the 500MB+ models she’d seen in online tutorials. She’d even bragged to her roommate over breakfast that