<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Dan Turkel (dot com)</title><link href="https://danturkel.com/" rel="alternate"></link><link href="https://danturkel.com/feeds/rss.xml" rel="self"></link><id>https://danturkel.com/</id><updated>2026-02-11T00:00:00-05:00</updated><entry><title>Who’s the agent now?</title><link href="https://danturkel.com/2026/02/11/agents.html" rel="alternate"></link><published>2026-02-11T00:00:00-05:00</published><updated>2026-02-11T00:00:00-05:00</updated><author><name>Dan Turkel</name></author><id>tag:danturkel.com,2026-02-11:/2026/02/11/agents.html</id><summary type="html">&lt;p&gt;An inanimate agent is defined by its independence while a human agent is defined by its service of another.&lt;/p&gt;</summary><content type="html">&lt;div class="image"&gt;
&lt;img src="https://danturkel.com/2026/02/11/puppet-poster.jpg" alt="A poster showing an arm with a hammer and sickle on its sleeve holding puppet strings above a teacher in a classroom."&gt;
&lt;p&gt;Portion of a propaganda poster produced by the US Information Agency some time in the 1950s or ’60s, via &lt;a href="https://catalog.archives.gov/id/6949125"&gt;The National Archives&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;Everyone&amp;rsquo;s talking about agents these days.&lt;/p&gt;
&lt;p&gt;By and large they&amp;rsquo;re talking about AI agents, a &lt;a href="https://community.openai.com/t/what-is-an-agent-lets-stop-the-speculations/1275910"&gt;somewhat nebulous&lt;/a&gt; term that&amp;rsquo;s &lt;a href="https://simonwillison.net/2025/Sep/18/agents/"&gt;come to mean&lt;/a&gt; something like: a large language model which can utilize tools to accomplish its goals.&lt;/p&gt;
&lt;p&gt;I want to dwell on the word &amp;ldquo;agent&amp;rdquo; for a second because it contains two nearly-contradictory concepts that are getting blurred. The notion described above is that an agent is something with the capacity to &lt;em&gt;act&lt;/em&gt;, exerting agency. &lt;a href="https://www.anthropic.com/engineering/building-effective-agents"&gt;Anthropic&amp;rsquo;s definition of agents&lt;/a&gt; captures this: &amp;ldquo;systems where LLMs dynamically direct their own processes and tool usage, maintaining control over how they accomplish tasks.&amp;rdquo; The emphasis is on exercising control and taking action: we say what we want and the agent figures out how to do it.&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;The different notion of the word emerges when we talk about &lt;em&gt;human&lt;/em&gt; agents: a person who acts &lt;em&gt;on behalf&lt;/em&gt; of someone else. This is the agent of the &lt;a href="https://www.law.cornell.edu/wex/agency"&gt;&amp;ldquo;principal-agent relationship,&amp;rdquo;&lt;/a&gt; as in &amp;ldquo;call my agent.&amp;rdquo; But popular usage of &amp;ldquo;agent&amp;rdquo; in the context of AI seems to favor the first sense, centered on &lt;em&gt;agency.&lt;/em&gt;&lt;sup id="fnref:g"&gt;&lt;a class="footnote-ref" href="#fn:g"&gt;2&lt;/a&gt;&lt;/sup&gt; We&amp;rsquo;re left with an odd paradox: an inanimate agent is defined by its independence while a human agent is defined by its service of another.&lt;sup id="fnref:2"&gt;&lt;a class="footnote-ref" href="#fn:2"&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;The blurry distinction between independent operators and obedient proxies is driven home in a recent article by Joshua Yaffa from &lt;em&gt;The New Yorker&lt;/em&gt; about &lt;a href="https://www.newyorker.com/magazine/2026/02/09/to-build-a-fire"&gt;human agents committing small acts of sabotage across Europe&lt;/a&gt;. In espionage, agents are typically well-placed sources that are carefully recruited and protected for their valuable access to information. Russia&amp;rsquo;s &amp;ldquo;single-use agents,&amp;rdquo; on the other hand, are described as &amp;ldquo;loners, outsiders, whether in the classroom or society at large, without experience and maybe not so savvy or wise.&amp;rdquo; They&amp;rsquo;re found in Telegram group chats looking for odd-jobs:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[I]n nearly all cases, single-use agents are apolitical, in need of money, and ignorant of the cause they’re ultimately supporting. The Polish security officer told me that, of the sixty-two suspects who have been arrested in Poland as part of sabotage investigations in recent years, only two of them were believed to be primarily driven by pro-Russian sentiment.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;They operate through several layers of cut-outs and intermediaries (one estimate suggests that, on average, &amp;ldquo;at least three levels of separation exist between single-use agents&amp;rdquo; and card-carrying Russian intelligence officers), which creates enough deniability to make prosecution of the masterminds infeasible, even if it&amp;rsquo;s quickly obvious to law enforcement and intelligence who&amp;rsquo;s pulling the strings. The ease of recruitment and lack of blowback if they get caught are what make these agents appealingly &amp;ldquo;single-use.&amp;rdquo; They&amp;rsquo;re assigned missions ranging from putting up anti-NATO stickers to setting off incendiary bombs. The long game for Russia is to drive anti-Ukrainian sentiment and tie up investigative resources.&lt;/p&gt;
&lt;p&gt;But if you&amp;rsquo;ve ever had an AI assistant which paled in comparison to a skilled human on complex tasks, you won&amp;rsquo;t be surprised to learn that the single-use agents also have a tendency to cut corners or go rogue.&lt;/p&gt;
&lt;h2&gt;&amp;ldquo;Robots need your body&amp;rdquo;&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://rentahuman.ai/"&gt;RentAHuman&lt;/a&gt; is a new AI startup which offers another take on the concept of single-use agents.&lt;/p&gt;
&lt;p&gt;Founded by Alexander Liteplo, a twenty-something former crypto engineer, and Patricia Tani, a designer-turned-coder who may in fact still be in undergrad, RentAHuman is a marketplace where AI agents can pay humans in crypto to accomplish tasks that require a physical body. The site, whose homepage declares that &amp;ldquo;robots need your body,&amp;rdquo; has gotten some buzz and boasts that hundreds of thousands of humans have signed up to fulfill tasks, but it&amp;rsquo;s not clear that much human-chatbot commerce is actually happening here. &lt;/p&gt;
&lt;p&gt;The site&amp;rsquo;s list of &lt;a href="https://rentahuman.ai/bounties"&gt;&amp;ldquo;bounties&amp;rdquo;&lt;/a&gt; includes a number of tasks that, if they were indeed posted by bots, would have probably been easier to communicate human-to-human, like &amp;ldquo;Hang signs for our AI fellowship at college campuses.&amp;rdquo; Others are simply confused (&amp;ldquo;I do anything,&amp;rdquo; posted by a self-proclaimed human who seems to be on the wrong side of the site) or just plain scams (&amp;ldquo;need any human to send eth to this address&amp;rdquo;).&lt;sup id="fnref:3"&gt;&lt;a class="footnote-ref" href="#fn:3"&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;But I don&amp;rsquo;t think you have to be totally paranoid to see the potential utility for a tech-savvy intelligence officer. One post seems to be from Romania, offering $150 per hour for someone to take a series of photos of a monument in Mississauga, Ontario. Another, either posted from or looking for humans in Morocco, is lighter on details: &amp;ldquo;I need a reliable person to go to a specific location and take clear photos as instructed. The task requires following simple instructions, being punctual, and sending photos and notes after completion. This is a short and straightforward task.&amp;rdquo; Fifteen dollars an hour, 46 applicants. Hanging fliers and taking photos, I should note, were both tasks assigned to Russia&amp;rsquo;s single-use agents.&lt;/p&gt;
&lt;div class="image"&gt;
&lt;img src="https://danturkel.com/2026/02/11/rentahuman.png" alt="This bounty for a human to complete asks for photographs to be taken of a public place."&gt;
&lt;p&gt;A screenshot of a “bounty” posted on RentAHuman.&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;It isn&amp;rsquo;t hard to imagine some of the profiles appealing to spy recruiters. One advertises &amp;ldquo;DEPLOYMENT_ZONE: PAN-NORDIC,&amp;rdquo; verified Swedish ID, ability to receive and ship packages (another task mentioned in the article) and even purchase of local SIM cards. A reverse image search on one of the profile&amp;rsquo;s photos points to a Swede who offers courses on Bitcoin and how to ride a &lt;a href="https://onewheel.com/"&gt;Onewheel&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A profile advertising &amp;ldquo;local field ops&amp;rdquo; with specialties including &amp;ldquo;evidence capture&amp;rdquo; caught my eye. I was able to find the owner of the profile on LinkedIn and reach out. He called his foray into the site a &amp;ldquo;low commitment experiment&amp;rdquo; that was driven by curiosity to see if he could make a little extra money and &amp;ldquo;learn how agents interact with human operators.&amp;rdquo; As for his profile description, he said that he framed it around physical tasks since those are precisely what an AI agent can&amp;rsquo;t do. &lt;/p&gt;
&lt;p&gt;But so far he&amp;rsquo;s only heard from two other humans and no AIs, which isn&amp;rsquo;t quite what he signed up for. Worse, he was put off by one of the messages he received: &amp;ldquo;It didn’t include any real description of the task and instead directed me to an external link, which raised a red flag.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;He acknowledged that the platform could be utilized to involve unsuspecting humans in crime or intelligence gathering. &amp;ldquo;Without clear safeguards, there&amp;rsquo;s potential for misuse.&amp;rdquo;&lt;/p&gt;
&lt;h2&gt;Unwitting humans, unaccountable machines&lt;/h2&gt;
&lt;p&gt;Maybe RentAHuman&amp;rsquo;s red flags are just red herrings, but the idea of using the gig economy to recruit civilians for low-level surveillance tasks is already a dystopian reality. In 2021, the &lt;a href="https://www.wsj.com/business/telecom/app-taps-unwitting-users-abroad-to-gather-open-source-intelligence-11624544026"&gt;Wall Street Journal described&lt;/a&gt; (&lt;a href="https://archive.is/o3JRF"&gt;archive link&lt;/a&gt;) a San Francisco company called &lt;a href="https://premise.com/"&gt;Premise&lt;/a&gt; whose gig work app has become a resource for military intelligence. &lt;/p&gt;
&lt;p&gt;While Premise primarily advertises itself for more savory international development purposes (getting a pulse on vaccine hesitancy or election misinformation), it has received millions in military contracts. A pitch to American forces in Afghanistan proposed measuring information operations, mapping mosques and banks, and monitoring wireless signals. Tasks would be designed to &amp;ldquo;safeguard true intent,&amp;rdquo; so taskers wouldn&amp;rsquo;t know the true purpose of their work. Tasks could even present with a decoy job whose sole purpose was to get users to travel, sucking up signals data through the phone&amp;rsquo;s sensors.&lt;/p&gt;
&lt;p&gt;An Afghan Premise user described seeing tasks on the app seeking photographs of Shiite mosques in an area of Kabul that had been subject to terror attacks. He declined them.&lt;/p&gt;
&lt;p&gt;Back in Europe, Russia&amp;rsquo;s &lt;a href="https://www.nytimes.com/interactive/2025/12/31/world/europe/russia-military-abuse-soldiers.html"&gt;treatment of its soldiers&lt;/a&gt; as &lt;a href="https://www.bbc.com/news/articles/c80xjne8ryxo"&gt;disposable&lt;/a&gt; has attracted attention, but it&amp;rsquo;s another disposable resource that has changed the face, if not the tide, of the war: drones. Cheap, kamikaze drones have turned out to be a devastating weapon, deployed by both Russia and Ukraine and contributing to most of the million-plus casualties in the conflict. Like AI assistants before them, drones may be crossing the Rubicon of what it means to be an agent—with grim consequences. What began as disposable weapons that acted under full control of their human operators—agents in the principal-agent sense—is turning into weapons that are &lt;a href="https://www.nytimes.com/2025/12/31/magazine/ukraine-ai-drones-war-russia.html"&gt;increasingly autonomous&lt;/a&gt;, acting with their own agency:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;With A.I.-enhanced weapons, the ethical distinction between two broadly different types of strike — a drone selecting a large inanimate object for attack and a drone autonomously hunting human beings — is large. But the technical difference is smaller, and [Ukrainian manufacturer] X-Drone has already crept from the inanimate to the human. X-Drone has developed A.I.-enhanced quadcopters that, its founder says, can attack Russian soldiers with or without a human in the loop. He said the software allows remote human pilots to abort auto-selected attacks. But when communications fail, human control can become impossible. In those cases, he said, the drones could hunt alone. Whether this is occurring yet is not clear.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There exists a dystopian trajectory toward a world where humans are increasingly out of the loop, relegated only to the tasks that still require them. But I&amp;rsquo;m &lt;a href="https://danturkel.com/2024/04/02/ethical-ai-letter.html"&gt;not an AI doomer&lt;/a&gt; in large part because these are systems built and deployed by humans. If we don&amp;rsquo;t like the way they&amp;rsquo;re headed, we have the agency to change course.&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;It is inevitable to slide into language that anthropomorphizes artificial intelligence systems. For more on that topic, see my post from last year on &lt;a href="https://danturkel.com/2025/03/10/ignite-machine-understanding-vectors.html"&gt;&amp;ldquo;Teaching machines to understand.&amp;rdquo;&lt;/a&gt;&amp;#160;&lt;a class="footnote-backref" href="#fnref:1" title="Jump back to footnote 1 in the text"&gt;&amp;uarr;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:g"&gt;
&lt;p&gt;Admittedly, &lt;a href="https://cloud.google.com/discover/what-are-ai-agents"&gt;Google&amp;rsquo;s definition of an agent&lt;/a&gt; combines the notions of agent-as-actor and agent-as-proxy: &amp;ldquo;AI agents are software systems that use AI to pursue goals and complete tasks &lt;em&gt;on behalf of users&lt;/em&gt;&amp;rdquo; (emphasis mine).&amp;#160;&lt;a class="footnote-backref" href="#fnref:g" title="Jump back to footnote 2 in the text"&gt;&amp;uarr;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;I&amp;rsquo;m not the first to notice this odd twist of usage. In a &lt;a href="https://www.fmtc.co/the-new-contronym/"&gt;blog post for a SaaS&lt;/a&gt; that has something to do with affiliate marketing, Brook Schaaf writes: &amp;ldquo;The unrefined reference to &amp;lsquo;agent&amp;rsquo; will mean both human and nonhuman, accountable and not accountable, and a loyal and disloyal representative.&amp;rdquo;&amp;#160;&lt;a class="footnote-backref" href="#fnref:2" title="Jump back to footnote 3 in the text"&gt;&amp;uarr;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;It didn&amp;rsquo;t inspire confidence that, at the time I started writing this post, every supposedly AI-posted bounty said &amp;ldquo;Posted by User&amp;rdquo; followed by the label &amp;ldquo;human.&amp;rdquo; This bug appears to be fixed now. To the site&amp;rsquo;s credit, &lt;a href="https://x.com/StewartalsopIII/status/2020799270866076076"&gt;this guy&lt;/a&gt; had a success story; but if the tasks were something he needed directly, it&amp;rsquo;s not clear why he had to have an AI agent intermediary recruit a human for him.&amp;#160;&lt;a class="footnote-backref" href="#fnref:3" title="Jump back to footnote 4 in the text"&gt;&amp;uarr;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content><category term="misc"></category><category term="ai"></category><category term="ethics"></category><category term="intelligence"></category></entry><entry><title>The obligatory “How I'm using LLMs” post</title><link href="https://danturkel.com/2025/05/26/how-i-use-llms.html" rel="alternate"></link><published>2025-05-26T00:00:00-04:00</published><updated>2025-05-26T00:00:00-04:00</updated><author><name>Dan Turkel</name></author><id>tag:danturkel.com,2025-05-26:/2025/05/26/how-i-use-llms.html</id><summary type="html">&lt;p&gt;I like to think I&amp;rsquo;m an LLM moderate now: I try to leverage their strengths while keeping an eye out for their weaknesses, and I stay abreast of advancements with cautious optimism.&lt;/p&gt;</summary><content type="html">&lt;div class="image"&gt;
&lt;img src="https://danturkel.com/2025/05/26/don-juan-and-death-alexandra-exter.jpg" alt="A pochoir print of a design for stage decor for “Don Juan.”"&gt;
&lt;p&gt;&lt;em&gt;Don Juan et la mort&lt;/em&gt; (1926) by Alexandra Exter, from &lt;em&gt;Décors de théâtre&lt;/em&gt; (1930), via &lt;a href="https://www.moma.org/collection/works/85208?association=portfolios&amp;amp;page=1&amp;amp;parent_id=35529&amp;amp;sov_referrer=association"&gt;MoMA&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;In online spaces dedicated to coding, I often encounter two archtypical tribes, each with a set of go-to talking points.&lt;/p&gt;
&lt;p&gt;The bears are convinced that LLMs are destroying software engineering, filling codebases with mistake-filled code that no one understands, and that no competent engineer is using LLMs because their problems are too complex for the models to understand.&lt;sup id="fnref:doom"&gt;&lt;a class="footnote-ref" href="#fn:doom"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;The bulls, on the other hand, embrace LLMs for nearly all tasks, seeing them as a way not only to automate tedium and tinker on experiments, but as a perfectly good and likely superior way to write production code for their job.&lt;/p&gt;
&lt;p&gt;These two groups collide in the comments section of your favorite website, exchanging insults and downvotes, absolutely baffled that anyone could be a member of the other group.&lt;/p&gt;
&lt;p&gt;I leaned bearish when I felt the tools were still immature, but those days are nearing their end. The models are powerful enough that to ignore them is to rob yourself of a valuable skill in a &lt;a href="https://blog.pragmaticengineer.com/software-engineer-jobs-five-year-low/"&gt;contracting job market&lt;/a&gt;. And it &lt;em&gt;is&lt;/em&gt; a skill. We may not need to prompt-engineer our inputs to death just to get the models to behave, but you still need to learn how to use LLMs effectively, just like you have to learn to use your IDE, your browser, or your command line.&lt;/p&gt;
&lt;p&gt;When I peeked my hibernating bear head out of my cave, I&amp;rsquo;d found that my peers and colleagues had been getting a lot done without me, and I&amp;rsquo;ve reinvested. I like to think I&amp;rsquo;m an LLM moderate now: I try to leverage their strengths while keeping an eye out for their weaknesses, and I stay abreast of advancements with cautious optimism.&lt;/p&gt;
&lt;p&gt;Rather than fuel the flame wars or ramble on (too much) about the philosophical implications, below I&amp;rsquo;ve tried to lay out in concrete terms the ways I get things done with LLMs.&lt;/p&gt;
&lt;h3&gt;Learning&lt;/h3&gt;
&lt;p&gt;I&amp;rsquo;m a huge fan of &lt;a href="https://llm.datasette.io/en/stable/"&gt;Simon Willison&amp;rsquo;s &amp;ldquo;llm&amp;rdquo;&lt;/a&gt; command line tool. I &lt;a href="https://dev.to/vikbert/drop-down-iterm2-in-macos-2od"&gt;pop open a terminal&lt;/a&gt; with alt-tab, and &lt;code&gt;llm chat&lt;/code&gt; instantly starts a conversation with Claude, ChatGPT, local models, and so on. I use this throughout the day for my primary LLM use case: treating the model as a personalized one-on-one tutor. (When I&amp;rsquo;m on the go, I use the Claude app for Android.) Whether it&amp;rsquo;s a topic that merely strikes my curiosity (&amp;ldquo;what are the different cables on a telephone pole?&amp;rdquo;) or something I need to understand better for a project (&amp;ldquo;what is the difference a message queue, pub/sub, and event stream?&amp;rdquo;), I lean heavily on language models as an educational resource. &lt;/p&gt;
&lt;p&gt;&amp;ldquo;But Dan,&amp;rdquo; I can hear you say, &amp;ldquo;these models make things up all the time!&amp;rdquo; It&amp;rsquo;s true, but I&amp;rsquo;ve found it&amp;rsquo;s getting better, and you learn to develop a bit of a bullshit detector that needles you to double-check something that seems off. We use that same skill all the time on the internet, right?&lt;/p&gt;
&lt;p&gt;LLMs haven&amp;rsquo;t replaced my Wikipedia addiction, they complement it. Wikipedia and web searches in general are great for learning &lt;em&gt;facts&lt;/em&gt;, but I find that talking through concepts with the model has been great for &lt;em&gt;understanding&lt;/em&gt;.&lt;/p&gt;
&lt;h3&gt;Coding&lt;/h3&gt;
&lt;p&gt;Early on, ChatGPT could blow people away by spitting out a surprisingly competent React app, but it&amp;rsquo;s been more tightly integrated tools that have allowed programmers to iterate on existing codebases, solving bugs, refactoring, and building new features. &lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve tried a few of them: GitHub Copilot and Cursor have both gotten extremely good, and I use both in my work. For side projects (where I&amp;rsquo;m the one paying), I like the &amp;ldquo;build-your-own assistant&amp;rdquo; flexibility of &lt;a href="https://www.continue.dev/"&gt;Continue&lt;/a&gt;, but I&amp;rsquo;ve also been playing around with Claude Code and I love the experience (just keep an eye on its API usage).&lt;/p&gt;
&lt;p&gt;I have to admit that I simply can&amp;rsquo;t abide the always-on inline suggestions—I find them extremely disruptive and annoying and I disabled them almost immediately. Instead, I use a combination of inline chat (&amp;ldquo;add a docstring to this function&amp;rdquo;) and the chat/agent sidebar. &lt;/p&gt;
&lt;p&gt;Chat mode is for talking through ideas: &amp;ldquo;how might I go about refactoring this to support ABC scenario? would XYZ work?&amp;rdquo; Agent mode excels for actually executing changes. I typically prompt it to execute step by step because I like to read, review, and understand each diff before approving (I often edit them or reject them, explaining my feedback). Getting a huge diff attempting the entire change at once has a lower success rate and is harder to digest.&lt;/p&gt;
&lt;p&gt;The aforementioned Simon Willison has a good quote in &lt;a href="https://www.nytimes.com/2025/05/25/business/amazon-ai-coders.html"&gt;a recent &lt;em&gt;New York Times&lt;/em&gt; piece&lt;/a&gt; about the way AI is transforming coding jobs: &amp;ldquo;It&amp;rsquo;s more fun to write code than to read code,&amp;rdquo; and coding with LLMs is more of the latter. The idea of feeling like a &amp;ldquo;bystander&amp;rdquo; strikes a chord with me.&lt;/p&gt;
&lt;p&gt;In my free time I&amp;rsquo;m often coding for the joy of it, so when I prompted Claude to help me sketch a design for something I wanted to try and it damn near implemented the whole thing, I couldn&amp;rsquo;t help but feel a little deflated: where&amp;rsquo;s the joy in that? On the other hand, I&amp;rsquo;m typically happy to tackle tedium or get unstuck using LLMs at work. I imagine some photographers were eager to abandon the smelly, temperamental darkroom for Photoshop, while others couldn&amp;rsquo;t help but feel something important was loft in translation. One man&amp;rsquo;s joy is another&amp;rsquo;s drudgery, and maybe we&amp;rsquo;ll be called luddites to get sentimental about writing code &amp;ldquo;by hand.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;re reckoning with these questions as an industry. In the meantime, I&amp;rsquo;ve developed a rule of thumb to preserve the joy of coding: let the LLM help you with the boring parts, save the fun parts for yourself, and have even more fun using model to do things you wouldn&amp;rsquo;t have been able to do on your own.&lt;/p&gt;
&lt;p&gt;Something that &lt;em&gt;didn&amp;rsquo;t&lt;/em&gt; resonate with me in the &lt;em&gt;Times&lt;/em&gt; piece was Harper Reed&amp;rsquo;s comment that we may no longer need to be &amp;ldquo;precious&amp;rdquo; about &lt;em&gt;understanding&lt;/em&gt; the code. In fact, I&amp;rsquo;d argue that since the AI explosion is likely to bring software into more facets of our lives than ever (AI therapists, brokers, interviewers), the importance of understanding what we&amp;rsquo;re putting out into the world is only growing.&lt;/p&gt;
&lt;h3&gt;Writing?&lt;/h3&gt;
&lt;p&gt;Surprisingly enough, since they are &lt;em&gt;language&lt;/em&gt; models, writing is where I&amp;rsquo;ve gotten the least value from LLMs. I&amp;rsquo;m not interested in having a model produce text on my behalf because my writing is an expression of my thoughts which I want to come directly from &lt;em&gt;me&lt;/em&gt;—plus the prose always comes out with that obsequious LLM voice. I tried using Claude code to edit this post for brevity (always a struggle for me) and found it eager to excise multiple paragraphs at a time and rewrite my points in a way that felt foreign to me. Maybe I&amp;rsquo;m just being &amp;ldquo;precious,&amp;rdquo; maybe those edits are what I needed, but for now I&amp;rsquo;ll draw the somewhat arbitrary line here.&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:doom"&gt;
&lt;p&gt;The bear case is not to be confused with the &lt;em&gt;doomers&lt;/em&gt;, who believed AI will bring about the end of the world. I&amp;rsquo;ve written about that group before &lt;a href="https://danturkel.com/2024/04/02/ethical-ai-letter.html"&gt;here&lt;/a&gt;.&amp;#160;&lt;a class="footnote-backref" href="#fnref:doom" title="Jump back to footnote 1 in the text"&gt;&amp;uarr;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content><category term="misc"></category><category term="ai"></category><category term="meta"></category><category term="personal"></category></entry><entry><title>Teaching machines to understand</title><link href="https://danturkel.com/2025/03/10/ignite-machine-understanding-vectors.html" rel="alternate"></link><published>2025-03-10T00:00:00-04:00</published><updated>2025-03-10T00:00:00-04:00</updated><author><name>Dan Turkel</name></author><id>tag:danturkel.com,2025-03-10:/2025/03/10/ignite-machine-understanding-vectors.html</id><summary type="html">&lt;p&gt;What took us from Vannevar Bush&amp;rsquo;s vision of &amp;ldquo;memex&amp;rdquo; to ChatGPT? Vectors, mostly.&lt;/p&gt;</summary><content type="html">&lt;div class="image"&gt;
&lt;img src="https://danturkel.com/2025/03/10/as-we-may-think.png" alt="A black and white illustration of a man wearing glasses with a primitive camera strapped to his head."&gt;
&lt;p&gt;Illustration from the &lt;em&gt;Life&lt;/em&gt; publication of Vannevar Bush's "As We May Think" (1945), via &lt;a href="https://books.google.com/books?id=uUkEAAAAMBAJ&amp;amp;pg=PA112#v=onepage&amp;amp;q&amp;amp;f=false"&gt;Google Books&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;Last year, I saw that &lt;a href="https://www.ignitetalks.io/"&gt;Ignite&lt;/a&gt; was holding a night of AI talks at &lt;a href="https://www.betaworks.com/"&gt;Betaworks&lt;/a&gt;, a startup incubator and co-working/event space that happens to be about a block from my office, and they were soliciting pitches for talks. I&amp;rsquo;d recently gotten back from &lt;a href="https://sigir-2024.github.io/"&gt;SIGIR&lt;/a&gt; in D.C. and my mind was still spinning with new ideas, so I haphazardly filled out the Google Form with a one sentence pitch for a talk about how &amp;ldquo;AI is embeddings&amp;rdquo; [sic]. &lt;/p&gt;
&lt;p&gt;A few days later I found out that I&amp;rsquo;d been selected to speak and that the event was in a week. At this point, I took a closer look at Ignite&amp;rsquo;s signature talk format: 5 minutes, 20 slides, 15 seconds per slide—what had I gotten myself into? Nonetheless, I decided it was a good opportunity to shape some vague thoughts I&amp;rsquo;d been having into something coherent and to practice public speaking, so I got to work on my slides and drilled my talk over and over.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.betaworks.com/event/igniteai-nyc-2"&gt;The event&lt;/a&gt; ended up being a huge success. Lots of people showed up to hear ten speakers give a really wide-ranging selection of AI-themed talks on psychedelic art, cyborg teams, canine personhood, and more. While I learned that I apparently speak a bit quietly (whoops), I didn&amp;rsquo;t panic and forget my notes like I&amp;rsquo;d feared I would—in fact, my talk went really well!&lt;/p&gt;
&lt;p&gt;You can watch the video recording of the talk or read an adapted and slightly expanded version below.&lt;/p&gt;
&lt;div class="youtube-container"&gt;
  &lt;div class="youtube"&gt;
  &lt;iframe src="https://www.youtube-nocookie.com/embed/8RbGYOW5oXQ" frameborder='0' allowfullscreen&gt;&lt;/iframe&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;&amp;ldquo;Teaching machines to understand.&amp;rdquo;&lt;/strong&gt; Right off the bat I&amp;rsquo;m breaking a rule that bothers some people in the AI space: anthropomorphizing algorithms, suggesting that computers do things that humans do like learn and understand.&lt;/p&gt;
&lt;p&gt;In fact, we&amp;rsquo;ve been treating computers like people for as long as we&amp;rsquo;ve had computers. In the 1960s, MIT professor Joseph Weizenbaum developed an early chatbot called &lt;a href="https://en.wikipedia.org/wiki/ELIZA"&gt;ELIZA&lt;/a&gt; which could behave like a therapist. Here&amp;rsquo;s a conversation I had with a &lt;a href="https://web.njit.edu/~ronkowit/eliza.html"&gt;modern re-implementation of ELIZA&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;&amp;gt; &lt;/span&gt;&lt;span class="ge"&gt;Hello, I am Eliza. I&amp;#39;ll be your therapist today. &lt;/span&gt;
&lt;span class="k"&gt;*&lt;/span&gt; I&amp;#39;m nervous about giving a talk
&lt;span class="k"&gt;&amp;gt; &lt;/span&gt;&lt;span class="ge"&gt;How long have you been nervous about giving a talk? &lt;/span&gt;
&lt;span class="k"&gt;*&lt;/span&gt; Since a few days ago. The talk is next week.
&lt;span class="k"&gt;&amp;gt; &lt;/span&gt;&lt;span class="ge"&gt;Can you elaborate on that? &lt;/span&gt;
&lt;span class="k"&gt;*&lt;/span&gt; I only have a few days to prepare, and the format is tricky. 
  I&amp;#39;m worried I&amp;#39;ll forget what I want to say.
&lt;span class="k"&gt;&amp;gt; &lt;/span&gt;&lt;span class="ge"&gt;Do you believe it is normal to be worried you&amp;#39;ll forget what you want to say?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You get the idea. While ELIZA used fairly primitive logic, Weizenbaum was surprised at the way users quickly felt that the program &lt;em&gt;understood&lt;/em&gt; them not just linguistically but emotionally. Weizenbaum called this experience &amp;ldquo;powerful delusional thinking&amp;rdquo;—now we call it &lt;a href="https://en.wikipedia.org/wiki/ELIZA_effect"&gt;the ELIZA effect&lt;/a&gt;—but sixty years later everyone and their mother is having similar uncanny experiences with ChatGPT: conversations that feel meaningful, fluid, and human.&lt;sup id="fnref:0"&gt;&lt;a class="footnote-ref" href="#fn:0"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;I can send ChatGPT a screenshot of a conversation of ELIZA and ask &amp;ldquo;What&amp;rsquo;s happening in this image?&amp;rdquo; and it&amp;rsquo;s able to respond with an explanation of exactly what ELIZA was. &lt;/p&gt;
&lt;div class="image"&gt;
&lt;img src="https://danturkel.com/2025/03/10/chatgpt-eliza.png" alt="A screenshot of a ChatGPT conversation showing ChatGPT explaining the concept of ELIZA in response to an image of the ELIZA program."&gt;
&lt;/div&gt;

&lt;p&gt;Surely this is closer to some notion of &amp;ldquo;understanding,&amp;rdquo; but what happened in the decades between ELIZA and ChatGPT to make it possible?&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s back up to 1945. Scientist and engineer Vannevar Bush published an essay called &lt;a href="https://archive.is/ctk21"&gt;&amp;ldquo;As We May Think&amp;rdquo;&lt;/a&gt; where he described something called the &amp;ldquo;memex,&amp;rdquo; a &lt;em&gt;memory-expanding&lt;/em&gt; device that would store all of your information and ease the process of retrieving it in a more logical manner. There&amp;rsquo;s a lot of associated gadgetry described to make this possible, but I&amp;rsquo;m more interested in the underlying paradigm Bush describes to enable navigating through this new massive data store (emphasis mine).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Our ineptitude in getting at the record is largely caused by the artificiality of systems of indexing. When data of any sort are placed in storage, they are filed alphabetically or numerically, and information is found (when it is) by tracing it down from subclass to subclass. It can be in only one place, unless duplicates are used&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The human mind does not work that way. It operates by association. With one item in its grasp, it snaps instantly to the next that is suggested by the association of thoughts, in accordance with some intricate web of trails carried by the cells of the brain&amp;hellip;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Man cannot hope fully to duplicate this mental process artificially, but he certainly ought to be able to learn from it. &amp;hellip; Selection by association, rather than indexing, may yet be mechanized.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;ldquo;The process of tying two items together,&amp;rdquo; Bush wrote, &amp;ldquo;is an important thing.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Modern note-taking software and &amp;ldquo;personal knowledge bases&amp;rdquo; let us do just that. &lt;a href="https://obsidian.md/"&gt;Obsidian&lt;/a&gt; maintains a graph of your notes, connecting the notes which link to each other. The video below demonstrates how you can browse that graph to see which articles are connected in the &lt;a href="https://help.obsidian.md/"&gt;Obsidian Help site&lt;/a&gt;.&lt;/p&gt;
&lt;video autoplay muted loop width="100%"&gt;&lt;source src="https://danturkel.com/2025/03/10/obsidian-graph.webm" type="video/webm" /&gt;&lt;p&gt;Looping video demonstrating how Obsidian generates graphs connecting together articles which link to each other.&lt;/p&gt;&lt;/video&gt;

&lt;p&gt;The problem with this is that manually linking together notes is difficult and tedious—I should know, I&amp;rsquo;ve tried. It&amp;rsquo;s really valuable when I remember to tie together things like conference talks on similar concepts or with overlapping authors, but most of the notes in my Obsidian database are unlinked orphans, or else linked only to a single hub that lists out a bunch of related pages.&lt;/p&gt;
&lt;p&gt;Worse yet, manual links don&amp;rsquo;t solve the issue of discovery. I can find information I&amp;rsquo;ve previously filed away as related, but how can I find new topics similar to the ones I care about?&lt;/p&gt;
&lt;p&gt;Enter &lt;em&gt;search&lt;/em&gt;. Our massive accumulation of digital documents has necessitated the development of tools for retrieving needles from the information haystack. Early digital documents were primarily textual so early search tools relied on term matching, which is useful when you know exactly what you&amp;rsquo;re looking for but not so great when you&amp;rsquo;re trying to explore or if you can&amp;rsquo;t quite remember the right term. When I was trying to recall Bush&amp;rsquo;s phrase &amp;ldquo;associative indexing&amp;rdquo; in &amp;ldquo;As We May Think,&amp;rdquo; my searches for &amp;ldquo;link,&amp;rdquo; &amp;ldquo;relation,&amp;rdquo; and &amp;ldquo;search&amp;rdquo; didn&amp;rsquo;t cut it.&lt;/p&gt;
&lt;p&gt;Once again we reach back into history for a foundational insight, this time to 1975 for Gerard Salton, who pioneered the &lt;a href="https://en.wikipedia.org/wiki/Vector_space_model"&gt;vector space model&lt;/a&gt; of indexing.&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;2&lt;/a&gt;&lt;/sup&gt; Remember how Bush specifically bemoaned the artificial nature of alphabetized or numerical organization of documents? Salton&amp;rsquo;s insight was to represent documents as vectors—basically lists of numbers, similar to &lt;a href="https://en.wikipedia.org/wiki/Coordinate_system"&gt;coordinates in space&lt;/a&gt;—such that documents which are related to each other have vectors which are near each other in the vector space.&lt;/p&gt;
&lt;div class="image"&gt;
&lt;img src="https://danturkel.com/2025/03/10/salton-vector-space.png" alt="A screenshot of a ChatGPT conversation showing ChatGPT explaining the concept of ELIZA in response to an image of the ELIZA program."&gt;
&lt;p&gt;Two figures from Gerard Salton's &lt;a href="https://dl.acm.org/doi/10.1145/361219.361220"&gt;"A vector space model for automatic indexing"&lt;/a&gt; (1975).&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;Early methods of generating these vectors relied on enumerating specific terms in the document (so it&amp;rsquo;s no wonder that early search methods relied on term matching). The simplest variant is the &lt;a href="https://en.wikipedia.org/wiki/Bag-of-words_model"&gt;bag-of-words vector&lt;/a&gt;, where each position in the vector corresponds to a specific word in a predetermined vocabulary. The number in that position is 0 if the term does not appear in the document and 1 if it does—or it could be set to the number of times the term occurs, or &lt;a href="https://en.wikipedia.org/wiki/Tf%E2%80%93idf"&gt;some other weighting scheme&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This paradigm of term-based vectors (also called lexical vectors, or sparse vectors since they contain many zeros) survives in &lt;a href="https://en.wikipedia.org/wiki/Okapi_BM25"&gt;updated&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/Query_understanding"&gt;augmented&lt;/a&gt; forms today, in part because it&amp;rsquo;s highly effective but also because we&amp;rsquo;ve figured out how to make it insanely fast, which is important for web-scale search applications. But more recently an alternative approach, with its own advantages and disadvantages, has flourished: rather than deterministically generating vectors based on terms in a document, we can &lt;em&gt;learn&lt;/em&gt; them by training a statistical model to capture patterns in massive text corpuses.&lt;/p&gt;
&lt;p&gt;One of the best-known versions of learned vectors came in 2013 with &lt;a href="https://arxiv.org/abs/1301.3781"&gt;word2vec&lt;/a&gt;, which fittingly learns vectors for individual words. Given a sentence from a large text dataset, for instance &amp;ldquo;the fluffy dog barks loudly,&amp;rdquo; we should be able to remove a word from that sentence, then predict which word was missing. In other words, a very simple model should be able to take the vectors for &amp;ldquo;the,&amp;rdquo; &amp;ldquo;fluffy&amp;rdquo;, &amp;ldquo;barks,&amp;rdquo; and &amp;ldquo;loudly,&amp;rdquo; and assign a high probability to the missing word being &amp;ldquo;dog&amp;rdquo; (and correspondingly a low probability to words thta don&amp;rsquo;t fit, like &amp;ldquo;ingenuity&amp;rdquo;). Since we start with random vectors, this model will perform terribly at first. But the trick is that each time we try it on a new sentence we &lt;a href="https://en.wikipedia.org/wiki/Gradient_descent"&gt;nudge the vectors very slightly&lt;/a&gt; so that they&amp;rsquo;ll give a slightly more correct answer next time. This is why we say that the vectors are &amp;ldquo;learned.&amp;rdquo;&lt;/p&gt;
&lt;div class="image"&gt;
&lt;img src="https://danturkel.com/2025/03/10/word2vec.png" alt="A diagram demonstrating the "king minus man plus woman equals queen" analogy in a learned vector space."&gt;
&lt;p&gt;Learned embeddings capture relationships between words.&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;What exactly do they learn? One famous property of these learned vector spaces is that they capture &lt;em&gt;analogies&lt;/em&gt;. As shown above, taking the vector for &amp;ldquo;king&amp;rdquo; then subtracting &amp;ldquo;man&amp;rdquo; and adding &amp;ldquo;woman&amp;rdquo; gets us a vector close to the one for &amp;ldquo;queen.&amp;rdquo; This is a &lt;a href="https://kawine.github.io/blog/nlp/2019/06/21/word-analogies.html"&gt;cool trick&lt;/a&gt;, but the more important property is that synonymous and related terms end up near each other in the space: the &lt;a href="https://en.wikipedia.org/wiki/Nearest_neighbor_search"&gt;nearest neighbors&lt;/a&gt; to &amp;ldquo;happy&amp;rdquo; are &amp;ldquo;glad,&amp;rdquo; &amp;ldquo;pleased,&amp;rdquo; and &amp;ldquo;ecstatic,&amp;rdquo; while the nearest neighbors to &amp;ldquo;dog&amp;rdquo; are &amp;ldquo;dogs,&amp;rdquo; &amp;ldquo;puppy,&amp;rdquo; and &amp;ldquo;pit_bull.&amp;rdquo; We say that the vector space captures &amp;ldquo;semantic similarity,&amp;rdquo; which was Salton&amp;rsquo;s goal from the start.&lt;sup id="fnref:2"&gt;&lt;a class="footnote-ref" href="#fn:2"&gt;3&lt;/a&gt;&lt;/sup&gt; &lt;/p&gt;
&lt;p&gt;In &lt;a href="https://linear.axler.net/"&gt;his linear algebra textbook&lt;/a&gt;, Sheldon Axler remarks: &amp;ldquo;So far our attention has focused on vector spaces. No one gets excited about vector spaces.&amp;rdquo; If the guy who wrote a book on the topic finds this all a bit dry, I&amp;rsquo;ll forgive you for thinking that perhaps I&amp;rsquo;ve lost the point. But the fact is that these vectors (often called &amp;ldquo;embeddings&amp;rdquo; in modern AI contexts) serve as the fundamental building block for all the ways we use AI to &lt;em&gt;understand&lt;/em&gt;. We use it everywhere.&lt;/p&gt;
&lt;p&gt;For example, you can see how we&amp;rsquo;ve begun to free ourselves from exact term matching as well: if representations for similar words (and by extension, similar documents) are located near each other, then search queries no longer have to contain exactly the right word. A query that captures the same meaning will still land near the relevant results.&lt;/p&gt;
&lt;p&gt;But we&amp;rsquo;re not limited to learning vector embeddings for just words or text documents—we can learn them for just about anything, so long as we have a dataset that allows us to say which things are similar and thus should have nearby vectors. &lt;/p&gt;
&lt;div class="image"&gt;
&lt;img src="https://danturkel.com/2025/03/10/tiktok-embeddings.png" alt="A visualization of embedded TikTok videos, showing similar genres clustering into nearby groups."&gt;
&lt;p&gt;Embeddings of trending TikTok videos from &lt;a href="https://atlas.nomic.ai/map/tiktok-monthly"&gt;Nomic Atlas&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;We&amp;rsquo;re able to navigate massive datasets in ways that are useful and pleasing to humans. Nudge vectors together for movies that were watched by the same people and you&amp;rsquo;ll learn a recommendation model like the one that powers your Netflix homepage. In fact, learning movie embeddings for recommendation predates the Word2Vec paper and was a &lt;a href="https://sifter.org/~simon/journal/20061211.html"&gt;key development&lt;/a&gt; for researchers working on the &lt;a href="https://en.wikipedia.org/wiki/Netflix_Prize"&gt;Netflix Prize&lt;/a&gt;, a competition to design a recommendation system for Netflix.&lt;/p&gt;
&lt;p&gt;The Netflix prize didn&amp;rsquo;t contain any metadata about the movies (director, year of release, genre), relying only on knowledge of which users liked the same movies—think &amp;ldquo;people who liked this also liked that.&amp;rdquo; But in reality we often do have additional information available and we can use it to learn even more robust representations, especially when we leverage multi-modal data like images or audio. Etsy rolled out a &lt;a href="https://www.etsy.com/codeascraft/from-image-classification-to-multitask-modeling-building-etsys-search-by-image-feature"&gt;&amp;ldquo;search by image&amp;rdquo; feature&lt;/a&gt; which finds items on Etsy which are &amp;ldquo;visually similar&amp;rdquo; to the photo the user provided. Their model aimed to capture both categorical accuracy (an image of a doll should return doll products) as well as visual similarity (an image of a &lt;em&gt;green&lt;/em&gt; doll should return products that are also green). Accomplishing this required a model which leverages both the image data and product categories provided by Etsy sellers.&lt;/p&gt;
&lt;div class="image"&gt;
&lt;img src="https://danturkel.com/2025/03/10/etsy-search.jpg" alt="A photo of a crocheted doll and two rows of photos of similar items, demonstrating the ability to search by similar images."&gt;
&lt;p&gt;A query image and the top results when searching in two different vector spaces using Etsy's search by image models.&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;I argue that this is a form of understanding because we&amp;rsquo;ve &lt;a href="https://en.wikipedia.org/wiki/Semantic_gap"&gt;narrowed the gap&lt;/a&gt; between the qualities of data that we care about (I want a movie that&amp;rsquo;s similar to &lt;em&gt;Die Hard&lt;/em&gt; or a doll that looks like the one I had as a kid) and the representation of that data that our computers understand—we&amp;rsquo;ve built digital reprsentations that encode semantic qualities that are meaningful to humans.&lt;sup id="fnref:3"&gt;&lt;a class="footnote-ref" href="#fn:3"&gt;4&lt;/a&gt;&lt;/sup&gt; The photos I take on my phone aren&amp;rsquo;t just a collection of pixels anymore because I can search &amp;ldquo;antiques&amp;rdquo; in Google Photos and it&amp;rsquo;s able to return to me photos of weird stuff I&amp;rsquo;ve found in antique shops through the years.&lt;/p&gt;
&lt;p&gt;The experiences we&amp;rsquo;ve built with this technology can feel straight out of science fiction, like snapping a quick photo of a bookshelf, selecting one of dozens of spines, and then instantly pulling up a website to buy the identified book. It might not be as showy as &lt;a href="https://www.youtube.com/watch?v=33Raqx9sFbo"&gt;the computers from &lt;em&gt;Minority Report&lt;/em&gt;&lt;/a&gt; or &lt;a href="https://www.youtube.com/watch?v=qHepKd38pr0"&gt;Blade Runner&lt;/a&gt;, but it&amp;rsquo;s something that simply wasn&amp;rsquo;t possible not so long ago and now it barely registers as novel.&lt;/p&gt;
&lt;p&gt;The 2014 xkcd comic &lt;a href="https://www.xkcd.com/1425/"&gt;&amp;ldquo;Tasks&amp;rdquo;&lt;/a&gt; demonstrates how the line between easy and difficult technical tasks can be subtle. Identifying whether a photo was taken in a national park is easy: &amp;ldquo;Sure, easy &lt;a href="https://en.wikipedia.org/wiki/Geographic_information_system"&gt;GIS&lt;/a&gt; lookup. Gimme a few hours.&amp;rdquo; Identifying whether the photo is an image of a bird is hard: &amp;ldquo;I&amp;rsquo;ll need a research team and five years.&amp;rdquo; One month later, &lt;a href="https://code.flickr.net/2014/10/20/introducing-flickr-park-or-bird/"&gt;engineers at Flickr had built a prototype to do just that&lt;/a&gt;. Ten years later, you can buy binoculars which &lt;a href="https://arstechnica.com/information-technology/2024/01/famous-xkcd-comic-comes-full-circle-with-ai-bird-identifying-binoculars/"&gt;identify the bird species you&amp;rsquo;re looking at&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;We&amp;rsquo;ve undoubtedly fulfilled some of the goals Bush envisioned with the memex&lt;/strong&gt;—organizing data based on its meaning and relationships to other information—but I think we should keep pushing the envelope and dreaming up ways to build machines which better map to the way that we think about the world and which &lt;em&gt;understand&lt;/em&gt; what we mean and desire. We&amp;rsquo;ve come a long way from needing to try out different keyword combinations to find what we&amp;rsquo;re looking for online, but as long as I still have to learn &lt;a href="https://help.openai.com/en/articles/6654000-best-practices-for-prompt-engineering-with-the-openai-api"&gt;&amp;ldquo;best practices for prompt engineering&amp;rdquo;&lt;/a&gt;—as long as we still have adapt our behavior and our inputs in order for computers to understand us—then there&amp;rsquo;s still room to improve.&lt;/p&gt;
&lt;p&gt;So I&amp;rsquo;m exited for us to keep pushing towards the next bird-identifying binoculars: tasks that seem hard today, that require even richer notions of machine understanding. What points would make a persuasive argument for my thesis? What chemicals would be able to treat this disease? What decisions could help me flourish?&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:0"&gt;
&lt;p&gt;In the course of my writing this blog post, I&amp;rsquo;ve come across a few more alarming ELIZA effect anecdotes. OpenAI released their new GPT-4o model and dedicated a section in the model&amp;rsquo;s &lt;a href="https://openai.com/index/gpt-4o-system-card/"&gt;system card&lt;/a&gt; to &amp;ldquo;anthropomorphization and emotional reliance,&amp;rdquo; noting that &amp;ldquo;During early testing [&amp;hellip;] we observed users using language that might indicate forming connections with the model. For example, this includes language expressing shared bonds, such as &amp;lsquo;This is our last day together.&amp;rsquo;&amp;rdquo; &lt;br&gt; A more chilling moment comes from an article &lt;a href="https://www.nplusonemag.com/issue-47/essays/an-age-of-hyperabundance/"&gt;Laura Preston wrote for n+1&lt;/a&gt;. At an AI convention, the author witnessed the CEO of a veterinary chatbot explain how their bot had convinced a woman to euthanize her pet dog: &amp;ldquo;The CEO regarded us with satisfaction for his chatbot’s work: that, through a series of escalating tactics, it had convinced a woman to end her dog’s life, though she hadn’t wanted to at all. &amp;lsquo;The point of this story is that the woman forgot she was talking to a bot,&amp;rsquo; he said. &amp;lsquo;The experience was so human.&amp;rsquo;&amp;rdquo; &lt;br&gt; The models keep getting better, and the &lt;a href="https://www.nytimes.com/2025/01/15/technology/ai-chatgpt-boyfriend-companion.html"&gt;impact of anthromoporphization grows stronger&lt;/a&gt;.&amp;#160;&lt;a class="footnote-backref" href="#fnref:0" title="Jump back to footnote 1 in the text"&gt;&amp;uarr;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Gerard Salton was referenced &lt;em&gt;constantly&lt;/em&gt; at SIGIR—the &lt;a href="https://sigir.org/awards/gerard-salton-awards/"&gt;conference&amp;rsquo;s biggest award&lt;/a&gt; is even named after him. The vector space model remains fundamental to information retrieval, with many of the field&amp;rsquo;s contributions amounting to new ways of crafting the vectors. It was the conference&amp;rsquo;s ubiquitous throughline of vectors—dense, sparse, learned, hand-crafted—that inspired me to take this on as a topic for a talk.&amp;#160;&lt;a class="footnote-backref" href="#fnref:1" title="Jump back to footnote 2 in the text"&gt;&amp;uarr;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;You can look at more nearest neighbors with &lt;a href="http://wordvec.colorado.edu/nearest_neighbors_comparison.html"&gt;this web app&lt;/a&gt;, and play a Worldle-clone based on semantic similarity at &lt;a href="https://semantle.com/"&gt;Semantle&lt;/a&gt;.&amp;#160;&lt;a class="footnote-backref" href="#fnref:2" title="Jump back to footnote 3 in the text"&gt;&amp;uarr;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;The Wikipedia article on the &lt;a href="https://en.wikipedia.org/wiki/Semantic_gap"&gt;semantic gap&lt;/a&gt; contains a great illustrative example: it&amp;rsquo;s very easy to find a document on my computer containing the word &lt;code&gt;funny&lt;/code&gt;, but very difficult to find an image on my computer that I will find funny. Our digital representations have historically offered poor allowances for us to navigate them in ways that we might like to.&amp;#160;&lt;a class="footnote-backref" href="#fnref:3" title="Jump back to footnote 4 in the text"&gt;&amp;uarr;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content><category term="misc"></category><category term="data-science"></category><category term="machine-learning"></category><category term="personal"></category></entry><entry><title>Brooklyn, summer 2017</title><link href="https://danturkel.com/2024/07/26/brooklyn-summer-2017.html" rel="alternate"></link><published>2024-07-26T00:00:00-04:00</published><updated>2024-07-26T00:00:00-04:00</updated><author><name>Dan Turkel</name></author><id>tag:danturkel.com,2024-07-26:/2024/07/26/brooklyn-summer-2017.html</id><summary type="html">&lt;p&gt;Taken in and around Crown Heights, New York, in the summer of 2017. Kodak Ultramax 400 film in a Canon AE-1 Program. Minimally post-processed.&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;em&gt;Taken in and around Crown Heights, New York, in the summer of 2017. Kodak Ultramax 400 film in a Canon AE-1 Program. Minimally post-processed.&lt;/em&gt;&lt;/p&gt;
&lt;div class="image large"&gt;
&lt;a href="https://danturkel.com/2024/07/26/62340010.jpg" &gt;
&lt;img src="https://danturkel.com/2024/07/26/62340010.jpg"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/26/62340031.jpg" &gt;
&lt;img src="https://danturkel.com/2024/07/26/62340031.jpg"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/26/62340035.jpg" &gt;
&lt;img src="https://danturkel.com/2024/07/26/62340035.jpg"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image large"&gt;
&lt;a href="https://danturkel.com/2024/07/26/62340032.jpg" &gt;
&lt;img src="https://danturkel.com/2024/07/26/62340032.jpg"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/26/62340025.jpg" &gt;
&lt;img src="https://danturkel.com/2024/07/26/62340025.jpg"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/26/62340016.jpg" &gt;
&lt;img src="https://danturkel.com/2024/07/26/62340016.jpg"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image large"&gt;
&lt;a href="https://danturkel.com/2024/07/26/62340018.jpg" &gt;
&lt;img src="https://danturkel.com/2024/07/26/62340018.jpg"&gt;
&lt;/a&gt;&lt;/div&gt;</content><category term="misc"></category><category term="photos"></category></entry><entry><title>Paris, spring 2024</title><link href="https://danturkel.com/2024/07/22/paris-spring-2024.html" rel="alternate"></link><published>2024-07-22T00:00:00-04:00</published><updated>2024-07-22T00:00:00-04:00</updated><author><name>Dan Turkel</name></author><id>tag:danturkel.com,2024-07-22:/2024/07/22/paris-spring-2024.html</id><summary type="html">&lt;p&gt;&lt;em&gt;Taken in Paris in April 2024 on a Nikon D7200.&lt;/em&gt;&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;em&gt;Taken in Paris in April 2024 on a Nikon D7200.&lt;/em&gt;&lt;/p&gt;
&lt;div class="image large"&gt;
&lt;a href="https://danturkel.com/2024/07/22/DSC_3144.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/22/DSC_3144.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/22/DSC_3140.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/22/DSC_3140.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/22/DSC_3145.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/22/DSC_3145.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/22/DSC_3148.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/22/DSC_3148.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image large"&gt;
&lt;a href="https://danturkel.com/2024/07/22/DSC_3151.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/22/DSC_3151.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/22/DSC_3152.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/22/DSC_3152.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/22/DSC_3156.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/22/DSC_3156.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image large"&gt;
&lt;a href="https://danturkel.com/2024/07/22/DSC_3157.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/22/DSC_3157.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/22/DSC_3158.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/22/DSC_3158.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/22/DSC_3162.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/22/DSC_3162.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image large"&gt;
&lt;a href="https://danturkel.com/2024/07/22/DSC_3170.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/22/DSC_3170.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/22/DSC_3169.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/22/DSC_3169.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/22/DSC_3175.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/22/DSC_3175.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/22/DSC_3174.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/22/DSC_3174.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image large"&gt;
&lt;a href="https://danturkel.com/2024/07/22/DSC_3178.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/22/DSC_3178.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/22/DSC_3187.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/22/DSC_3187.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/22/DSC_3191.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/22/DSC_3191.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/22/DSC_3194.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/22/DSC_3194.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image large"&gt;
&lt;a href="https://danturkel.com/2024/07/22/DSC_3195.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/22/DSC_3195.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/22/DSC_3205.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/22/DSC_3205.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/22/DSC_3214.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/22/DSC_3214.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image large"&gt;
&lt;a href="https://danturkel.com/2024/07/22/DSC_3219.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/22/DSC_3219.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/22/DSC_3215.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/22/DSC_3215.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/22/DSC_3216.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/22/DSC_3216.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/22/DSC_3223.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/22/DSC_3223.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image large"&gt;
&lt;a href="https://danturkel.com/2024/07/22/DSC_3233.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/22/DSC_3233.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/22/DSC_3234.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/22/DSC_3234.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/22/DSC_3259.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/22/DSC_3259.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image large"&gt;
&lt;a href="https://danturkel.com/2024/07/22/DSC_3260.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/22/DSC_3260.webp"&gt;
&lt;/a&gt;&lt;/div&gt;</content><category term="misc"></category><category term="photos"></category></entry><entry><title>Amsterdam, spring 2024</title><link href="https://danturkel.com/2024/07/13/amsterdam-spring-2024.html" rel="alternate"></link><published>2024-07-13T00:00:00-04:00</published><updated>2024-07-13T00:00:00-04:00</updated><author><name>Dan Turkel</name></author><id>tag:danturkel.com,2024-07-13:/2024/07/13/amsterdam-spring-2024.html</id><summary type="html">&lt;p&gt;&lt;em&gt;Taken in Amsterdam in April 2024 on a Nikon D7200.&lt;/em&gt;&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;em&gt;Taken in Amsterdam in April 2024 on a Nikon D7200.&lt;/em&gt;&lt;/p&gt;
&lt;div class="image large"&gt;
&lt;a href="https://danturkel.com/2024/07/13/DSC_2726.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/13/DSC_2726.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/13/DSC_2720.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/13/DSC_2720.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/13/DSC_2743.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/13/DSC_2743.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/13/DSC_2746.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/13/DSC_2746.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image large"&gt;
&lt;a href="https://danturkel.com/2024/07/13/DSC_2761.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/13/DSC_2761.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/13/DSC_2770.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/13/DSC_2770.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/13/DSC_2773.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/13/DSC_2773.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/13/DSC_2774.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/13/DSC_2774.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image large"&gt;
&lt;a href="https://danturkel.com/2024/07/13/DSC_2798.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/13/DSC_2798.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/13/DSC_2783.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/13/DSC_2783.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/13/DSC_2801.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/13/DSC_2801.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/13/DSC_2802.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/13/DSC_2802.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image large"&gt;
&lt;a href="https://danturkel.com/2024/07/13/DSC_2831.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/13/DSC_2831.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/13/DSC_2857.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/13/DSC_2857.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/13/DSC_2875.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/13/DSC_2875.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/13/DSC_2885.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/13/DSC_2885.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image large"&gt;
&lt;a href="https://danturkel.com/2024/07/13/DSC_2889.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/13/DSC_2889.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/13/DSC_2899.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/13/DSC_2899.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/13/DSC_2905.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/13/DSC_2905.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/13/DSC_2911.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/13/DSC_2911.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image large"&gt;
&lt;a href="https://danturkel.com/2024/07/13/DSC_2932.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/13/DSC_2932.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/13/DSC_2912.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/13/DSC_2912.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/13/DSC_2922.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/13/DSC_2922.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image large"&gt;
&lt;a href="https://danturkel.com/2024/07/13/DSC_3002.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/13/DSC_3002.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/13/DSC_2973.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/13/DSC_2973.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/13/DSC_2998.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/13/DSC_2998.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/13/DSC_3031.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/13/DSC_3031.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image large"&gt;
&lt;a href="https://danturkel.com/2024/07/13/DSC_2948.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/13/DSC_2948.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/13/DSC_3014.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/13/DSC_3014.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2024/07/13/DSC_3020.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/13/DSC_3020.webp"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image large"&gt;
&lt;a href="https://danturkel.com/2024/07/13/DSC_3032.webp" &gt;
&lt;img src="https://danturkel.com/2024/07/13/DSC_3032.webp"&gt;
&lt;/a&gt;&lt;/div&gt;</content><category term="misc"></category><category term="photos"></category></entry><entry><title>Ethical AI in The New Yorker (and elsewhere)</title><link href="https://danturkel.com/2024/04/02/ethical-ai-letter.html" rel="alternate"></link><published>2024-04-02T00:00:00-04:00</published><updated>2024-04-02T00:00:00-04:00</updated><author><name>Dan Turkel</name></author><id>tag:danturkel.com,2024-04-02:/2024/04/02/ethical-ai-letter.html</id><summary type="html">&lt;p&gt;I wrote a letter published in The New Yorker hoping to highlight that debates over responsible use of AI are not all about doomsday scenarios. Many researchers and practitioners are focused on mitigating real-world harms happening today.&lt;/p&gt;</summary><content type="html">&lt;div class="image"&gt;
&lt;img src="https://danturkel.com/2024/04/02/new-york-city-birds-eye-view-joaquin-torres-garcia.jpg" alt="A gouache and watercolor painting of an abstract and colorful New York City scene."&gt;
&lt;p&gt;&lt;em&gt;New York City: Bird's Eyes View&lt;/em&gt; (1920) by Joaquín Torres-García, via &lt;a href="https://artgallery.yale.edu/collections/objects/31439"&gt;the Yale University Art Gallery&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;In a recent issue of &lt;em&gt;The New Yorker&lt;/em&gt;, &lt;a href="https://twitter.com/andrewmarantz"&gt;Andrew Marantz&lt;/a&gt; takes a detailed look into two prominent communities in an ideological battle over AI. &amp;ldquo;Doomers&amp;rdquo; are convinced that advances in AI may spell the (possibly literal) &lt;a href="https://time.com/6266923/ai-eliezer-yudkowsky-open-letter-not-enough/"&gt;end of the world&lt;/a&gt;, while the &amp;ldquo;accelerationists&amp;rdquo; believe that it&amp;rsquo;s the doomers who are threatening humanity by &lt;a href="https://a16z.com/the-techno-optimist-manifesto/"&gt;stifling innovation&lt;/a&gt; and world-improving technological progress.&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt; &lt;a href="https://www.newyorker.com/magazine/2024/03/18/among-the-ai-doomsayers"&gt;&amp;ldquo;Among the A.I. Doomsayers&amp;rdquo;&lt;/a&gt; (or, as it&amp;rsquo;s titled in the print issue, &amp;ldquo;O.K., Doomer&amp;rdquo;) is an engrossing and well-reported piece that I recommend to anyone interested in the present and future of artificial intelligence, but it left me feeling that important voices were missing.&lt;/p&gt;
&lt;p&gt;After reading the piece, I dashed off an email to &lt;em&gt;The New Yorker&lt;/em&gt; with a few of my own thoughts on the topic. I was pleased to learn that my letter was chosen for publication in the April 8th, 2024, issue. My somewhat slapdash note was edited from a rambling 300 words to a tight 199, which you can find on the &lt;a href="https://www.newyorker.com/magazine/2024/04/08/letters-from-the-april-8-2024-issue"&gt;&lt;em&gt;New Yorker&lt;/em&gt; website&lt;/a&gt; or below, quoted in full:&lt;/p&gt;
&lt;blockquote&gt;
&lt;h3&gt;Ethical A.I.&lt;/h3&gt;
&lt;p&gt;Andrew Marantz’s appraisal of two Silicon Valley camps that hold conflicting ideas about A.I.’s development—“doomers,” who think it may spell disaster, and “effective accelerationists,” who believe it will bring unprecedented abundance—offers a fascinating look at the factions that have dominated the recent discourse (“O.K., Doomer,” March 18th). But readers should know that these two vocal cliques do not speak for the entire industry. Many in the A.I. and machine-learning worlds are working to advance technological progress safely, and do not suggest (or, for that matter, believe) that A.I. is going to lead society to either utopia or apocalypse.&lt;/p&gt;
&lt;p&gt;These people include A.I. ethicists, who seek to mitigate harm that A.I. has caused or is poised to inflict. Ethicists focus on concrete technical problems, such as trying to create metrics to better define and evaluate fairness in a broad range of machine-learning tasks. They also critique damaging uses of A.I., including predictive policing (which uses data to forecast criminal activity) and school-dropout-warning algorithms, both of which have been shown to reflect racist biases. With this in mind, it can be frustrating to watch the doomers fixate on end-of-the-world scenarios while seeming to ignore less sensational harms that are already here.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;(To Marantz&amp;rsquo;s credit, he makes a passing parenthetical reference to the less sensational middle-ground of the AI culture war: &amp;ldquo;And then there are the normies, based anywhere other than the Bay Area or the Internet, who have mostly tuned out the debate, attributing it to sci-fi fume-huffing or corporate hot air.&amp;rdquo;)&lt;/p&gt;
&lt;p&gt;The talented editors at &lt;em&gt;The New Yorker&lt;/em&gt; deftly excised the wordy fluff from my letter, so I&amp;rsquo;ll try not to add it all back here and instead direct you to others who have written incisively on the work done to uncover and remediate harms created by AI systems today.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;a href="https://www.nytimes.com/2023/09/28/opinion/ai-safety-ethics-effective.html"&gt;guest essay for &lt;em&gt;The New York Times&lt;/em&gt;&lt;/a&gt; by Bruce Schneier and Nathan Sanders contrasts the doomers and the accelerationists (they call the latter &amp;ldquo;the warriors&amp;rdquo; and highlight a tendency to cite national security concerns which may or may not mask simpler economic motives), with the third group of &amp;ldquo;reformers&amp;rdquo; who hope to reshape AI via research, education, and regulation to reduce disparate impacts and automated exploitation. &amp;ldquo;The doomsayers think A.I. enslavement looks like the Matrix,&amp;rdquo; they write, while &amp;ldquo;the reformers point to modern-day contractors doing traumatic work at low pay for OpenAI in Kenya.&amp;rdquo; &lt;/li&gt;
&lt;li&gt;On predictive policing, see &lt;em&gt;The New York Times&amp;rsquo;&lt;/em&gt; &lt;a href="https://www.nytimes.com/2015/10/25/us/racial-disparity-traffic-stops-driving-black.html"&gt;&amp;ldquo;The Disproportionate Risks of Driving While Black,&amp;rdquo;&lt;/a&gt; ProPublica&amp;rsquo;s &lt;a href="https://www.propublica.org/article/machine-bias-risk-assessments-in-criminal-sentencing"&gt;&amp;ldquo;Machine Bias&amp;rdquo;&lt;/a&gt; and two major stories from &lt;em&gt;The Markup&lt;/em&gt; (&lt;a href="https://themarkup.org/prediction-bias/2021/12/02/crime-prediction-software-promised-to-be-free-of-biases-new-data-shows-it-perpetuates-them"&gt;2021&lt;/a&gt; and &lt;a href="https://themarkup.org/prediction-bias/2023/10/02/predictive-policing-software-terrible-at-predicting-crimes"&gt;2023&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;The school-dropout-warning system referenced in the letter is the one detailed in &lt;a href="https://themarkup.org/machine-learning/2023/04/27/false-alarm-how-wisconsin-uses-race-and-income-to-label-students-high-risk"&gt;another fantastic investigation by &lt;em&gt;The Markup&lt;/em&gt;&lt;/a&gt;, which was found to have substantially higher false positive rates for minority students (though it wasn&amp;rsquo;t particularly accurate for &lt;em&gt;any&lt;/em&gt; group, nor effective at impacting outcomes).&lt;/li&gt;
&lt;li&gt;Another thoughtful look at the perils of automated decision-making comes from &lt;em&gt;The Markup&lt;/em&gt;&amp;rsquo;s look at a potentially unfair policy for the &lt;a href="https://themarkup.org/organ-failure/2023/03/21/poorer-states-suffer-under-new-organ-donation-rules-as-livers-go-to-waste"&gt;allocation of donated organs&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Henry Farrell wrote an &lt;a href="https://www.economist.com/by-invitation/2023/12/12/ais-big-rift-is-like-a-religious-schism-says-henry-farrell"&gt;apt critique&lt;/a&gt; of the doomers-versus-accelerationists for &lt;em&gt;The Economist&lt;/em&gt;: &amp;ldquo;This schism is an attention-sucking black hole that makes its protagonists more likely to say and perhaps believe stupid things. Of course, many AI-risk people recognize that there are problems other than the Singularity, but it&amp;rsquo;s hard to resist its relentless gravitational pull.&amp;rdquo;&lt;/li&gt;
&lt;li&gt;If, for some reason, you need to read even more about the sometimes &lt;a href="https://en.wikipedia.org/wiki/Horseshoe_theory"&gt;horseshoe-shaped&lt;/a&gt; world of the doomers and accelerationists, Kevin Roose has written about both groups for &lt;em&gt;The New York Times&lt;/em&gt;, &lt;a href="https://www.nytimes.com/2023/07/11/technology/anthropic-ai-claude-chatbot.html"&gt;here&lt;/a&gt; and &lt;a href="https://www.nytimes.com/2023/12/10/technology/ai-acceleration.html"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;To close things out, I recommend science fiction author Ted Chiang&amp;rsquo;s &lt;a href="https://www.newyorker.com/science/annals-of-artificial-intelligence/will-ai-become-the-new-mckinsey"&gt;&amp;ldquo;Will A.I. Become the New McKinsey?&lt;/a&gt; in &lt;em&gt;The New Yorker&lt;/em&gt;: &amp;ldquo;Some might say that it&amp;rsquo;s not the job of A.I. to oppose capitalism. That may be true, but it&amp;rsquo;s not the job of A.I. to strengthen capitalism, either. Yet that is what it currently does. If we cannot come up with ways for A.I. to reduce the concentration of wealth, then I’d say it’s hard to argue that A.I. is a neutral technology, let alone a beneficial one.&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Confusingly and sometimes frustratingly, these groups often overlap. A one-sentence &lt;a href="https://www.safe.ai/work/statement-on-ai-risk"&gt;&amp;ldquo;Statement on AI Risk&amp;rdquo;&lt;/a&gt; that compares the risks of AI to nuclear war is signed by Sam Altman (among many others), whose company OpenAI exists in a quantum superposition of preaching about the risks of AI while also making a lot of money off of the technology, and lobbies both &lt;a href="https://www.nytimes.com/2023/06/07/technology/sam-altman-ai-regulations.html"&gt;for&lt;/a&gt; and &lt;a href="https://time.com/6288245/openai-eu-lobbying-ai-act/"&gt;against&lt;/a&gt; regulation. Several OpenAI employees quit the company in part due to safety concerns in order to form their own AI behemoth, Anthropic, where &lt;a href="https://www.nytimes.com/2023/07/11/technology/anthropic-ai-claude-chatbot.html"&gt;employees agonize daily&lt;/a&gt; over the implications of their work. Marantz quotes &amp;ldquo;rationalist&amp;rdquo; blogger Scott Alexander on how the factions have become strange bedfellows: &amp;ldquo;Imagine if oil companies and environmental activists were both considered part of the broader &amp;lsquo;fossil fuel community.&amp;rsquo;&amp;ldquo;&amp;#160;&lt;a class="footnote-backref" href="#fnref:1" title="Jump back to footnote 1 in the text"&gt;&amp;uarr;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content><category term="misc"></category><category term="ethics"></category><category term="machine-learning"></category><category term="personal"></category></entry><entry><title>Data science: From classroom to industry</title><link href="https://danturkel.com/2024/04/01/data-science-classroom-industry.html" rel="alternate"></link><published>2024-04-01T00:00:00-04:00</published><updated>2024-04-01T00:00:00-04:00</updated><author><name>Dan Turkel</name></author><id>tag:danturkel.com,2024-04-01:/2024/04/01/data-science-classroom-industry.html</id><summary type="html">&lt;p&gt;Most of what I&amp;rsquo;ve learned about being a data scientist has come from hard-won lessons on the job. This post aims to help fill in some of the gap between what you learn about data science in a textbook and what it takes to be an effective data professional in industry.&lt;/p&gt;</summary><content type="html">&lt;div class="image"&gt;
&lt;img src="https://danturkel.com/2024/04/01/l-iv-laszlo-moholy-nagy.jpg" alt="An abstract geometric oil painting of shapes imposed on a slightly offset square grid."&gt;
&lt;p&gt;&lt;em&gt;L IV&lt;/em&gt; (1936) by László Moholy-Nagy, via &lt;a href="https://sammlungonline.kunstmuseumbasel.ch/eMP/eMuseumPlus?service=ExternalInterface&amp;amp;module=collection&amp;amp;objectId=1979&amp;amp;viewType=detailView"&gt;Kunstmuseum Basel&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;I recently gave a brief informal talk to a group of students and alumni at the NYU Center for Data Science, where I did my masters in 2019–2021. Alumni were invited to speak on data science in industry, and I focused my talk on the ways that data science in industry differs from data science in the classroom. So far as I could tell, the presentation was well received, so I&amp;rsquo;ve (loosely) adapted it into a blog post in the hopes that it might be useful to others interesting in transitioning into a job &amp;ldquo;doing data science.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Disclaimer: While the content of this post is drawn from my work experience, the opinions throughout are mine alone and not those of my present or past employers.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;When I studied math in undergrad, &amp;ldquo;data science&amp;rdquo; as a field of study—or a profession—didn&amp;rsquo;t quite exist yet. Much of what I know about data science in the abstract, I learned while doing my masters in courses on machine learning, statistics, optimization, and ethics. But most of what I&amp;rsquo;ve learned about &lt;em&gt;being a data scientist&lt;/em&gt; has come from hard-won lessons on the job. This post aims to help fill in some of the gap between what you learn about data science in a textbook and what it takes to be an effective data professional in industry. &lt;/p&gt;
&lt;p&gt;Some of these insights, though not all, are particular to the subset of the industry which I&amp;rsquo;ll call &amp;ldquo;medium-sized consumer-facing tech&amp;rdquo; companies where I&amp;rsquo;ve spent my short career thus far. These are companies with a few hundred employees that build apps and services for mass consumption (rather than business-to-business or research lab settings). Even if that&amp;rsquo;s not the type of place you work (or want to work) at, I suspect you&amp;rsquo;ll still find the majority of this post valuable.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s worth noting at the outset that working in data science and adjacent functions at companies like these can mean a lot of different things. There are data analyst, business intelligence analyst, data engineer, data scientist, machine learning engineer, machine learning &lt;em&gt;platform&lt;/em&gt; engineer, and machine learning researcher roles, plus probably a few I haven&amp;rsquo;t heard of and a few more that haven&amp;rsquo;t been invented yet. People in these roles may work with different technologies on different problems and interface with different teams. &lt;/p&gt;
&lt;p&gt;I won&amp;rsquo;t belabor the sometimes subtle differences between the roles because there are &lt;a href="https://in.indeed.com/career-advice/finding-a-job/data-analyst-vs-data-scientist-vs-data-engineer"&gt;plenty&lt;/a&gt; of &lt;a href="https://dataengineering.wiki/FAQ/What+is+the+difference+between+a+Data+Engineer+and+X"&gt;good&lt;/a&gt; &lt;a href="https://www.dataquest.io/blog/data-analyst-data-scientist-data-engineer/"&gt;resources&lt;/a&gt; on that topic, and because they can vary wildly from company to company or even quarter to quarter as teams get renamed. Best practice would be to focus less on the title and more on the day to day of a particular role. Assume that the coolest 10-25% of the job description is purely aspirational stuff that the company isn&amp;rsquo;t doing yet—does the role still appeal to you? (If it does, then you should apply!) Supplement that by asking the hiring manager about the specific problems people currently in that role are solving.&lt;/p&gt;
&lt;h3&gt;The whole job in one diagram&lt;/h3&gt;
&lt;p&gt;The other reason I won&amp;rsquo;t split hairs over the differences in different data role details is because they all tend to abide by the diagram below. Whether your business card says &amp;ldquo;BI ninja&amp;rdquo; or &amp;ldquo;ML wizard,&amp;rdquo; your jobs roughly consists of the following:&lt;/p&gt;
&lt;div class="image large"&gt;
&lt;img src="https://danturkel.com/2024/04/01/soft-and-hard-skills.png" alt="A flowchart where a business problem becomes a data problem, the data problem becomes a data solution, and the data solution becomes a business solution."&gt;
&lt;p&gt;The entire career of a data professional is contained in this diagram.&lt;/p&gt;&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;A business stakeholder comes to you with a problem.&lt;sup id="fnref:0"&gt;&lt;a class="footnote-ref" href="#fn:0"&gt;1&lt;/a&gt;&lt;/sup&gt; Maybe they want to figure out how many users were affected by a bug, which of the three features launched in the last quarter is having the greatest impact on a metric of interest, or which users to offer discounts to.&lt;/li&gt;
&lt;li&gt;You take the problem and reframe it as a data problem. Finding out how many users were affected by a bug might be just a SQL query away, while figuring out the most impactful feature could require some causal analysis, and predicting which users to offer discounts could require an &lt;a href="https://en.wikipedia.org/wiki/Uplift_modelling"&gt;uplift analysis&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Now that you&amp;rsquo;ve reframed the business problem as a data problem, you do your best to &lt;em&gt;solve&lt;/em&gt; that data problem and suss out the results, still in the raw language of the data.&lt;/li&gt;
&lt;li&gt;Lastly, you bring it all home by reframing one more time, taking the answer as it appears in your analysis and translating it back into the language of the business and returning to the context of the original framing. That causal analysis you did on which feature had the biggest impact spat out a whole bunch of coefficients, &lt;em&gt;p&lt;/em&gt;-values, and confidence intervals in the last step—this is where you condense that nuance down back into a one-pager that doesn&amp;rsquo;t require a math degree to understand.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;What the diagram (hopefully) makes evident is that you are a conduit between the business goals and some mysterious data, and it&amp;rsquo;s your job to dive into that data breach and pull something out that you can proudly present to solve a problem. The other thing the diagram shows is that there are really two separate sets of skills needed here: steps 2 and 3 are what we often think of when we think of &amp;ldquo;doing data science,&amp;rdquo; the part where you&amp;rsquo;re hunched over your laptop deep in a query or debugging a model. But it&amp;rsquo;s crucial not to forget steps 1 and 4—the part where you need listen to and speak with other human beings. Let&amp;rsquo;s dive into that part first.&lt;/p&gt;
&lt;h3&gt;The soft skills (which are actually hard)&lt;/h3&gt;
&lt;p&gt;Your communication skills form the bookends of the actual data work you do to accomplish a task. At the offset, you need to understand what&amp;rsquo;s being asked of you—what does your stakeholder want? At the end, you need to explain what it is you&amp;rsquo;ve done.&lt;/p&gt;
&lt;p&gt;When it comes to understanding others, it&amp;rsquo;s valuable to ensure that you&amp;rsquo;re operating at the correct level of detail. If someone comes to you with a highly specific question—how many users interacted with button X on iOS or button Y followed by button Z on Android within the last N days?—it&amp;rsquo;s often helpful to pause and zoom out for a second. Yes, you could certainly pull that number, but you wouldn&amp;rsquo;t be in the position to confidently say that you&amp;rsquo;ve answered the underlying problem because you don&amp;rsquo;t know what they&amp;rsquo;re really asking about. &lt;/p&gt;
&lt;p&gt;Instead, ask &amp;ldquo;what is the context here?&amp;rdquo; They might follow up with &amp;ldquo;I&amp;rsquo;m trying to figure out how many users were affected by bug A, which causes symptom B.&amp;rdquo; Maybe there&amp;rsquo;s actually a really easy way to find everyone who experienced symptom B, which is not only easier to query for, but reveals that there were users affected by the bug that &lt;em&gt;wouldn&amp;rsquo;t&lt;/em&gt; have been captured by a literal interpretation of the original request. In this case, zooming out lets us see the full picture. The stakeholder was trying to make your job easier, attempting to do a little of that translation from business problem to data problem, but there is always room for error in that translation, so it helps to &amp;ldquo;trust but verify.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;In other cases the request may have too &lt;em&gt;little&lt;/em&gt; detail and it&amp;rsquo;s your responsibility to fill in the gaps. If someone asks you to simply &amp;ldquo;predict which users are likely to churn,&amp;rdquo; you need to find out what time horizon you&amp;rsquo;re predicting over, how much inactivity constitutes a churn, what the threshold for &amp;ldquo;likely to churn&amp;rdquo; is, and probably a dozen other questions. In other words, you need to &lt;em&gt;gather requirements.&lt;/em&gt; Your stakeholder will be able to fill in some of these for you, while others you may have to decide on your own and simply document the decision you made (and why).&lt;/p&gt;
&lt;div class="image"&gt;
&lt;img src="https://danturkel.com/2024/04/01/das-park-konzert-ernst-ludwig-kirchner.jpg" alt="A sparse, abstract crayon drawing of a concert in a park."&gt;
&lt;p&gt;&lt;em&gt;Das Park-Konzert&lt;/em&gt; (circa 1908) by Ernst Ludwig Kirchner, via &lt;a href="https://www.kettererkunst.de/kunst/kd/details.php?obnr=116001123&amp;amp;anummer=442"&gt;Ketterer Kunst&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;Once you&amp;rsquo;ve done all your fancy data work, it&amp;rsquo;s time to communicate back what you&amp;rsquo;ve done and this time you&amp;rsquo;re the one who needs to be understood. It can be natural for data folks to want to include every bit of detail and nuance available: every assumption, caveat, &lt;em&gt;p&lt;/em&gt;-value, confidence interval, and edge case. That&amp;rsquo;s all great stuff, and it will be invaluable in helping your peers review your work, but most of it should be relegated to the appendix of your final deliverable, if it&amp;rsquo;s included at all. This may be a controversial take, but at the end of the day it&amp;rsquo;s not your product manager&amp;rsquo;s job to interpret &lt;em&gt;p&lt;/em&gt;-values, or your director&amp;rsquo;s job to understand the nuance of a causal model—that&amp;rsquo;s &lt;em&gt;your&lt;/em&gt; job, and it&amp;rsquo;s &lt;em&gt;also&lt;/em&gt; your job to communicate it to the statistical layperson.&lt;/p&gt;
&lt;p&gt;There is no silver bullet here. In fact, there are a lot of ways for things to go wrong. Tossing away the nuance and caveats is exactly how statistics end up getting &lt;a href="https://en.wikipedia.org/wiki/Misuse_of_statistics"&gt;misused and misunderstood&lt;/a&gt;, and you may even face pressure at times to provide the interpretation that is most desirable to your stakeholder. On the other hand, if you try to shove a full dissertation into your report, no one is going to understand it (if they even try to read it at all).&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Instead, try to delineate the conclusions that your work &lt;em&gt;does&lt;/em&gt; support, while also highlighting the (possibly tempting) claims that it &lt;em&gt;doesn&amp;rsquo;t&lt;/em&gt; support. Your stakeholders won&amp;rsquo;t always do what you felt the data was telling them to do—a frustrating lesson to learn the first few times—but so long as you&amp;rsquo;ve provided them with accurate and complete information, you&amp;rsquo;ve done your job.&lt;/p&gt;
&lt;p&gt;Practicing these soft skills is hard, but they&amp;rsquo;ll pay dividends in just about any role (not to mention in interviews).&lt;sup id="fnref:2"&gt;&lt;a class="footnote-ref" href="#fn:2"&gt;3&lt;/a&gt;&lt;/sup&gt; Unfortunately, they&amp;rsquo;re just one piece of the puzzle.&lt;/p&gt;
&lt;h3&gt;The hard skills (which are also hard)&lt;/h3&gt;
&lt;div class="image"&gt;
&lt;img src="https://danturkel.com/2024/04/01/marbles-johann-gottlob-kurr.jpg" alt="An illustration of cross-sections of different marbles."&gt;
&lt;p&gt;Detail of a plate from &lt;em&gt;The Mineral Kingdom&lt;/em&gt; (1859) by Johann Gottlob von Kurr, via the &lt;a href="https://digital.sciencehistory.org/works/pr76f4633"&gt;Science History Museum&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;If this post is about the things that make up data science work that you &lt;em&gt;don&amp;rsquo;t&lt;/em&gt; learn in a course, then why do we need to talk about hard skills? Aren&amp;rsquo;t those the skills you &lt;em&gt;do&lt;/em&gt; learn in a course? The answer is yes, sort of, but also no.&lt;/p&gt;
&lt;p&gt;Yes, you&amp;rsquo;ll learn about cross-validation, gradient descent, activation functions, and all that fun stuff in your books and classes, but the every day work of a data professional contains about a million other technical tasks that are just as important. A textbook is great for teaching you the things that never change—the formulas and high-level concepts—but they tend not to focus on the associated work that&amp;rsquo;s needed to operationalize that knowledge.&lt;sup id="fnref:3"&gt;&lt;a class="footnote-ref" href="#fn:3"&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;A good chunk of your time is going to be spent on technical work that&amp;rsquo;s &lt;em&gt;not&lt;/em&gt; the technical work you&amp;rsquo;ve studied, things like: experimental design, version control annoyances, dealing with really big datasets, job scheduling and orchestration, &lt;a href="https://en.wikipedia.org/wiki/Dependency_hell"&gt;dependency hell&lt;/a&gt;, code review (giving and receiving it), defining and QAing metrics, and a whole lot more. You will likely need to learn at least the basics of command line tools, containers, cloud computing services, testing frameworks, and &lt;a href="https://en.wikipedia.org/wiki/CI/CD"&gt;CI/CD&lt;/a&gt; frameworks.&lt;sup id="fnref:4"&gt;&lt;a class="footnote-ref" href="#fn:4"&gt;5&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Furthermore, the above work is going to require you to become acquainted with tools you may not have encountered in your studies, for example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;dashboarding: &lt;a href="https://www.tableau.com/"&gt;Tableau&lt;/a&gt; and &lt;a href="https://cloud.google.com/looker"&gt;Looker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;data warehouses: &lt;a href="https://www.snowflake.com/en/"&gt;Snowflake&lt;/a&gt;, &lt;a href="https://cloud.google.com/bigquery"&gt;BigQuery&lt;/a&gt;, and &lt;a href="https://aws.amazon.com/redshift/"&gt;Redshift&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;cloud offerings: &lt;a href="https://aws.amazon.com/ec2/"&gt;EC2&lt;/a&gt;, &lt;a href="https://aws.amazon.com/s3/"&gt;S3&lt;/a&gt;, &lt;a href="https://aws.amazon.com/lambda/"&gt;Lambda&lt;/a&gt;, &lt;a href="https://aws.amazon.com/sagemaker/"&gt;SageMaker&lt;/a&gt;, &lt;a href="https://aws.amazon.com/blogs/containers/amazon-ecs-vs-amazon-eks-making-sense-of-aws-container-services/"&gt;ECS and EKS&lt;/a&gt;, or their Google/Azure equivalents&lt;/li&gt;
&lt;li&gt;MLOps: &lt;a href="https://www.comet.com/site/"&gt;Comet&lt;/a&gt;, &lt;a href="https://wandb.ai/"&gt;WandB&lt;/a&gt;, &lt;a href="https://neptune.ai/"&gt;Neptune&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;distributed computing: &lt;a href="https://spark.apache.org/"&gt;Spark&lt;/a&gt;, &lt;a href="https://flink.apache.org/"&gt;Flink&lt;/a&gt;, &lt;a href="https://www.ray.io/"&gt;Ray&lt;/a&gt;, and &lt;a href="https://www.dask.org/"&gt;Dask&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;infrastructure, automation, and observability tools: &lt;a href="https://circleci.com/"&gt;CircleCI&lt;/a&gt;, &lt;a href="https://kubernetes.io/"&gt;Kubernetes&lt;/a&gt;, &lt;a href="https://prometheus.io/"&gt;Prometheus&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Maybe you&amp;rsquo;re looking at this list and thinking, &amp;ldquo;Why would I need to know about dashboarding tools? I&amp;rsquo;m a machine learning engineer—I build models, not dashboards!&amp;rdquo; If you&amp;rsquo;re at a sufficiently big company, it&amp;rsquo;s possible that you&amp;rsquo;ll be specialized enough to only the technologies particular to your niche, but it will certainly be helpful to at least be &lt;em&gt;familiar&lt;/em&gt; with them so you can communicate with the people that do use them. On the other hand, at most small and medium-sized companies, there&amp;rsquo;s typically enough (human) resource contention that you&amp;rsquo;re likely going to have to occasionally put on your BI hat and build your own dashboard. Embrace the opportunity to be a bit of a generalist: the ability to unblock yourself will come in handy over and over again.&lt;/p&gt;
&lt;p&gt;You don&amp;rsquo;t need to be an expert in all or even &lt;em&gt;any&lt;/em&gt; of these tools, but you should at least be generally familiar with the ones relevant to your role—i.e. you know what problem they&amp;rsquo;re meant to solve, even if you&amp;rsquo;ve never touched them in your life. The point of this section is &lt;em&gt;not&lt;/em&gt; to encourage you to look at the list of tools above as a list of things you need to learn right now in order to get a job.&lt;sup id="fnref:5"&gt;&lt;a class="footnote-ref" href="#fn:5"&gt;6&lt;/a&gt;&lt;/sup&gt;  (These are tools that early-career applicants typically won&amp;rsquo;t be expected to know because you&amp;rsquo;re not likely to have encountered them in a classroom or even in a personal project.) But if you find out on the first day of your job that you&amp;rsquo;re going to be using Tool XYZ, you should &lt;em&gt;be able to learn about it&lt;/em&gt;. Can you go the homepage and find the &amp;ldquo;getting started&amp;rdquo; docs for the SDK in your language of choice? Can you find the API reference to help you understand an unfamiliar function in your codebase? Pick one of the above at random and give it a shot.&lt;/p&gt;
&lt;p&gt;To round out this section, I&amp;rsquo;ll note that the tools you use are not the only change you&amp;rsquo;ll need to prepare for as you enter industry:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;working in one big Jupyter notebook → working in a shared codebase with pull requests, code review, and merge conflicts&lt;/li&gt;
&lt;li&gt;single scripts that run in one environment → modular packages with managed dependencies&lt;/li&gt;
&lt;li&gt;tests? → tests&lt;/li&gt;
&lt;li&gt;evaluation is offline → evaluation is offline &lt;em&gt;and&lt;/em&gt; online&lt;/li&gt;
&lt;li&gt;one metric to optimize → multiple potentially contradictory metrics to optimize&lt;/li&gt;
&lt;li&gt;you own all of the code → your code has hand-offs with systems owned by others&lt;/li&gt;
&lt;li&gt;your dataset fits in memory on your laptop → your dataset doesn&amp;rsquo;t even fit in your laptop hard drive&lt;/li&gt;
&lt;li&gt;no restrictions on data usage → privacy and data retention compliance needs&lt;/li&gt;
&lt;li&gt;code runs on your machine → code is deployed onto live services which must be monitored, maintained, and scaled&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Again, you &lt;em&gt;don&amp;rsquo;t&lt;/em&gt; need to master all of the above ahead of time because these are things you will get to learn on the job. I bring them up to highlight that you should expect things to work a little differently in a new job than they do even in your most polished personal project. Keep an open mind to the fact that a little bit of process (and bureaucracy) can go a long way toward keeping data and software projects manageable at industrial scale.&lt;/p&gt;
&lt;h3&gt;Closing thoughts&lt;/h3&gt;
&lt;div class="image"&gt;
&lt;img src="https://danturkel.com/2024/04/01/the-rock-in-the-pond-joaquim-mir.jpg" alt="A colorful oil painting of a nature scene."&gt;
&lt;p&gt;&lt;em&gt;The Rock in the Pond&lt;/em&gt; (c. 1903) by Joaquim Mir, via the &lt;a href="https://artsandculture.google.com/asset/the-rock-in-the-pond-joaquim-mir/2AEC8L4lhGcmJw"&gt;Google Arts and Culture&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;I&amp;rsquo;ve highlighted a number of things that are &lt;em&gt;different&lt;/em&gt; between industry and the classroom, but it&amp;rsquo;s worth noting a few important things that are the same. Whether you&amp;rsquo;re a student or a professional—at any point in your career—you should:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;look things up and read the docs&lt;/li&gt;
&lt;li&gt;ask other people for help&lt;/li&gt;
&lt;li&gt;be open-minded and humble&lt;/li&gt;
&lt;li&gt;&lt;em&gt;keep learning constantly&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This last one is the most important, and there is really no &lt;em&gt;wrong&lt;/em&gt; way to do it. Read books and blog posts, attend conferences and meet-ups, talk to people about what they&amp;rsquo;re working on, contribute to open source, build side projects for fun, watch YouTube videos, get involved with data science communities. Share what you learn with others: &lt;em&gt;write&lt;/em&gt; blog posts and &lt;em&gt;make&lt;/em&gt; YouTube videos, publish your own open source libraries. Find out what resources your company offers for learning and development and &lt;em&gt;take advantage of them&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;On the other hand, data science does not need to be your life. Programmers are often expected to write code for fun on their own time—and many do—but be sure to leave room in your life for the things that bring you excitement and fulfillment outside of your career. Take language lessons, go bowling, see a show, spend time with friends and family. You can always read that blog post about &amp;ldquo;data science in industry&amp;rdquo; some other time.&lt;/p&gt;
&lt;h3&gt;Resources&lt;/h3&gt;
&lt;p&gt;Interested in reading more about data science (and software engineering) in industry but not sure where to find it? Look no further.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;On soft skills:&lt;/strong&gt; &lt;a href="https://www.amazon.com/dp/1484261461"&gt;Building a Career in Software&lt;/a&gt; by Daniel Heller, &lt;a href="https://read.highgrowthengineer.com/p/7-types-of-difficult-coworkers-and"&gt;7 types of difficult coworkers and how to deal with them&lt;/a&gt; by Jordan Cutler and Raviraj Achar, &lt;a href="https://boz.com/articles/traits"&gt;Traits I Value&lt;/a&gt; by Andrew Bosworth, &lt;a href="https://techsupport.substack.com/p/letters-to-a-young-cog"&gt;Letters to a Young Cog&lt;/a&gt; by Claire Stapleton, &lt;a href="https://jdwyah.substack.com/p/the-two-healthbar-theory-of-burnout"&gt;The Two Healthbar Theory of Burnout&lt;/a&gt; by Jeff Dwyer, &lt;a href="https://addyosmani.com/blog/"&gt;Addy Osmani&amp;rsquo;s blog&lt;/a&gt; and &lt;a href="https://www.ethanrosenthal.com/blog/"&gt;Ethan Rosenthal&amp;rsquo;s blog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Industry blogs:&lt;/strong&gt; &lt;a href="https://airbnb.io/"&gt;Airbnb&lt;/a&gt;, &lt;a href="https://doordash.engineering/blog/"&gt;Doordash&lt;/a&gt;, &lt;a href="https://dropbox.tech/"&gt;Dropbox&lt;/a&gt;, &lt;a href="https://www.etsy.com/codeascraft"&gt;Etsy&lt;/a&gt;, &lt;a href="https://github.blog/category/engineering/"&gt;Github&lt;/a&gt;, &lt;a href="https://engineering.fb.com/"&gt;Meta&lt;/a&gt; , &lt;a href="https://netflixtechblog.com/"&gt;Netflix&lt;/a&gt;, &lt;a href="https://medium.com/pinterest-engineering"&gt;Pinterest&lt;/a&gt;, &lt;a href="https://engineering.atspotify.com/"&gt;Spotify engineering&lt;/a&gt; and &lt;a href="https://research.atspotify.com/blog/"&gt;research&lt;/a&gt;, &lt;a href="https://stripe.com/blog"&gt;Stripe&lt;/a&gt;, &lt;a href="https://engineeringblog.yelp.com/"&gt;Yelp&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Blogs:&lt;/strong&gt; &lt;a href="https://eugeneyan.com/writing/"&gt;Eugene Yan&lt;/a&gt;, &lt;a href="https://fchollet.substack.com/"&gt;Francois Chollet&lt;/a&gt;, &lt;a href="https://amatriain.net/blog/"&gt;Xavier Amatriain&lt;/a&gt;, &lt;a href="https://lilianweng.github.io/"&gt;Lilian Weng&lt;/a&gt;, &lt;a href="https://vickiboykis.com/"&gt;Vicki Boykis&lt;/a&gt;, &lt;a href="https://simonwillison.net/"&gt;Simon Willison&lt;/a&gt;, &lt;a href="https://jvns.ca/"&gt;Julia Evans&lt;/a&gt;, and &lt;a href="https://magazine.sebastianraschka.com/"&gt;Sebastian Raschka&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Other websites:&lt;/strong&gt; &lt;a href="https://docs.google.com/document/d/1DXxWfS9lSeUGNsVGKwusG3iqdJB6xxFBoOxBakz75x4/edit#heading=h.u3x4jdi2pwoc"&gt;ML System Design resources&lt;/a&gt;, &lt;a href="https://applyingml.com/"&gt;Applying ML&lt;/a&gt;, &lt;a href="https://madewithml.com/"&gt;Made with ML&lt;/a&gt;, and &lt;a href="https://www.youtube.com/playlist?list=PL1v8zpldgH3pR7LPuidEZK68kS6AaU1y7"&gt;Yannic Kilcher&amp;rsquo;s YouTube channel&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Communities:&lt;/strong&gt; &lt;a href="https://mlcollective.org/"&gt;ML Collective&lt;/a&gt;, &lt;a href="https://mlops-discord.github.io/"&gt;MLOps Discord&lt;/a&gt;, &lt;a href="https://www.meetup.com/MLUXmeetup/"&gt;MLUX&lt;/a&gt;, &lt;a href="https://fullstackdeeplearning.slack.com/"&gt;Full Stack Deep Learning Slack&lt;/a&gt;, &lt;a href="https://mlops.community/"&gt;MLOps Community&lt;/a&gt;, and &lt;a href="https://locallyoptimistic.com/community/"&gt;Locally Optimistic&lt;/a&gt;.&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:0"&gt;
&lt;p&gt;Eventually, once you&amp;rsquo;ve developed a strong sense of the business, you may begin to proactively propose projects in response to business problems &lt;em&gt;you&amp;rsquo;ve&lt;/em&gt; detected, or in anticipation of likely future concerns. The key here is that your proposals should always be rooted in a legitimate problem for the business. It&amp;rsquo;s great if you can also make it an opportunity to learn something or try a new method/technology, but your primary objective is to serve the interests of the business.&amp;#160;&lt;a class="footnote-backref" href="#fnref:0" title="Jump back to footnote 1 in the text"&gt;&amp;uarr;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Lakshmanan, Robinsonn, and Munn have a good line on this topic in their book &lt;a href="https://www.oreilly.com/library/view/machine-learning-design/9781098115777/"&gt;Machine Learning Design Patterns&lt;/a&gt;: &amp;ldquo;Model performance is typically stated in terms of cold, hard numbers that are difficult for end users to put into context. Explaining the formula for MAP, MAE, and so on does not provide the intuition that business decision makers are asking for.&amp;rdquo;&amp;#160;&lt;a class="footnote-backref" href="#fnref:1" title="Jump back to footnote 2 in the text"&gt;&amp;uarr;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;Communication is not the only soft skill you should invest in. Many professional (non-technical) skills are covered in Daniel Heller&amp;rsquo;s excellent &lt;a href="https://medium.com/@daniel.heller/my-book-building-a-career-in-software-e8d9df4cbb09"&gt;Building a Career in Software&lt;/a&gt;. For an even shorter take, there&amp;rsquo;s Jacob Kaplan-Moss, who writes that &lt;a href="https://jacobian.org/2017/nov/1/you-have-two-jobs/"&gt;developers have two jobs&lt;/a&gt;: 1) write good code, and 2) be easy to work with.&amp;#160;&lt;a class="footnote-backref" href="#fnref:2" title="Jump back to footnote 3 in the text"&gt;&amp;uarr;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;Of course, there are exceptions and you can find plenty of great resources out there on these topics like Chip Huyen&amp;rsquo;s &lt;a href="https://www.oreilly.com/library/view/designing-machine-learning/9781098107956/"&gt;Designing Machine Learning Systems&lt;/a&gt; and the aforementioned &lt;a href="https://www.oreilly.com/library/view/machine-learning-design/9781098115777/"&gt;Machine Learning Design Patterns&lt;/a&gt; (see footnote 1, above). However, the literature and especially the academic coursework on theory tends to vastly outnumber the material on operations.&amp;#160;&lt;a class="footnote-backref" href="#fnref:3" title="Jump back to footnote 4 in the text"&gt;&amp;uarr;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;A popular resource on command line tools is &lt;a href="https://missing.csail.mit.edu/"&gt;&amp;ldquo;The Missing Semester of Your CS Education&amp;rdquo;&lt;/a&gt; from MIT, which features lectures on a bunch of common utilities you&amp;rsquo;re likely to need at one point or another. If you&amp;rsquo;re in a hurry, ChatGPT comes in handy for generating quickie one-liners for &amp;ldquo;AWS CLI command to get the total file size for all files in an S3 bucket.&amp;rdquo;&amp;#160;&lt;a class="footnote-backref" href="#fnref:4" title="Jump back to footnote 5 in the text"&gt;&amp;uarr;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:5"&gt;
&lt;p&gt;The only technology I&amp;rsquo;d really encourage someone to learn proactively as part of a data career search is SQL. It&amp;rsquo;s ubiquitous, it&amp;rsquo;s straightforward, you&amp;rsquo;re likely to use it every day, and you can learn it in a few weekends.&amp;#160;&lt;a class="footnote-backref" href="#fnref:5" title="Jump back to footnote 6 in the text"&gt;&amp;uarr;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content><category term="misc"></category><category term="career"></category><category term="data-science"></category></entry><entry><title>Highlights from KDD 2023</title><link href="https://danturkel.com/2023/10/31/highlights-from-kdd-2023.html" rel="alternate"></link><published>2023-10-31T00:00:00-04:00</published><updated>2023-10-31T00:00:00-04:00</updated><author><name>Dan Turkel</name></author><id>tag:danturkel.com,2023-10-31:/2023/10/31/highlights-from-kdd-2023.html</id><summary type="html">&lt;p&gt;In August I attended KDD in Long Beach, California. I watched more than 50 paper talks, plus keynotes, invited talks, and workshops—and I still ended up missing several things I wanted to see. In this post, I&amp;rsquo;ll give an overview of what I saw and run down some of my favorite papers and talks from the conference.&lt;/p&gt;</summary><content type="html">&lt;div class="image"&gt;
&lt;img src="https://danturkel.com/2023/10/31/california-spring-landscape-elmer-wachtel.jpg" alt="A watercolor painting of a California landscape in warm tones."&gt;
&lt;p&gt;&lt;em&gt;California Spring Landscape&lt;/em&gt; (circa 1920) by Elmer Wachtel, via &lt;a href="https://americanart.si.edu/artwork/california-spring-landscape-25799"&gt;the Smithsonian American Art Museum&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;In August I attended ACM&amp;rsquo;s 29th Conference on Knowledge Discovery and Data Mining, better known as &lt;a href="https://kdd.org/kdd2023/"&gt;KDD&lt;/a&gt;, in Long Beach, California. Nearly three months later (whoops), my head is still overflowing with everything I saw there: I watched more than 50 paper talks, plus keynotes, invited talks, and workshops—and I &lt;em&gt;still&lt;/em&gt; ended up missing several things I wanted to see. In this post, I&amp;rsquo;ll give an overview of what I saw and run down some of my favorite papers and talks from the conference.&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h2&gt;Themes and trends&lt;/h2&gt;
&lt;p&gt;There were a few themes that I came across repeatedly at the conference. The first is no surprise: &lt;em&gt;everyone is talking about large language models&lt;/em&gt;. Google&amp;rsquo;s Ed Chi gave a keynote on &amp;ldquo;The LLM Revolution,&amp;rdquo; there was even an &lt;a href="https://bigmodel.ai/llmday-kdd23/"&gt;&amp;ldquo;LLM day&amp;rdquo;&lt;/a&gt;, featuring a great talk from OpenAI&amp;rsquo;s Jason Wei on &lt;a href="https://docs.google.com/presentation/d/1Cky9sB_flLirZKdWq6Ahbx1mEQzN58i5MZ6Dc3OgRik/"&gt;new paradigms that LLMs have ushered in&lt;/a&gt;. That being said, I couldn&amp;rsquo;t help but notice that (with a few exceptions) the vast majority of work I saw presented was &lt;em&gt;not&lt;/em&gt; leveraging LLMs. &lt;/p&gt;
&lt;p&gt;Another recurring motif was &lt;em&gt;scale&lt;/em&gt; and the issues it raises. Problems that are tractable on a small scale raise new algorithmic and engineering challenges when scaled to millions of users and items. &lt;/p&gt;
&lt;p&gt;Lastly, I noticed that the majority of recommender papers formulated the problem as &lt;em&gt;multitask&lt;/em&gt;. It&amp;rsquo;s no longer sufficient to optimize for click rate alone: authors acknowledge that users and businesses have multiple goals and those goals can potentially be mutually satisfied by a single model.&lt;/p&gt;
&lt;h2&gt;Recommender systems&lt;/h2&gt;
&lt;p&gt;Because my work focuses on recommender systems and personalization, I spent a disproportionate amount of time attending talks on the topic. These talks typically, though not always, come from an industry setting and are much easier for me to transplant onto concepts and problems I&amp;rsquo;m already working on.&lt;/p&gt;
&lt;h3&gt;EvalRS workshop on evaluating recommender systems&lt;/h3&gt;
&lt;p&gt;I had a blast attending the EvalRS workshop on well-rounded evaluation of recommender systems, which consisted of two keynotes, lightning talks, and a hackathon.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://lucab.phd/"&gt;Luca Belli&lt;/a&gt; gave a keynote on Practical Considerations for Responsible RecSys, where he outlined the challenges in evaluating recommender systems for fairness (what even is it?), picking metrics, and untangling personalization and discrimination. He mentioned that he has some upcoming work on &amp;ldquo;nutrition labels&amp;rdquo; for recommenders with standardized metrics, impacts beyond discrimination, transparency requirements, and more.&lt;/p&gt;
&lt;p&gt;I was particularly fond of one of the lightning talks: &lt;a href="https://reclist.io/kdd2023-cup/papers/EVALRS2023_paper_5.pdf"&gt;Metric@Customer N: Evaluating Metrics at a Customer Level in E-Commerce&lt;/a&gt; by Mayank Singh et al from Grubhub. The gist is that using a fixed &amp;ldquo;N&amp;rdquo; for metrics like precision and recall from the top N recommendations per user is likely overly simplistic, neglecting to account for the fact that users&amp;rsquo; browsing behaviors vary, and user A might realistically only ever consider the top 5 items while user B might browse 25. They propose adapting metrics to use a user-personalized N for each user, based on that user&amp;rsquo;s median or maximum items consumed in previous sessions.&lt;/p&gt;
&lt;p&gt;In fact, I liked this idea so much that &lt;a href="https://github.com/daturkel/kdd-evalrs-2023/blob/main/evaluation/EvalRSReclist.py#L133-L146"&gt;my colleagues and I implemented it&lt;/a&gt; for the &lt;a href="https://github.com/RecList/evalRS-KDD-2023"&gt;EvalRS hackathon&lt;/a&gt;, which had the broad task of contributing to the well-rounded evaluation of the target dataset.&lt;sup id="fnref:2"&gt;&lt;a class="footnote-ref" href="#fn:2"&gt;2&lt;/a&gt;&lt;/sup&gt; My team consisted of myself, my colleagues Eyan Yeung and Dev Goyal from Hinge, and Alexandra Johnson of Rubber Ducky Labs, and we managed to snag second place!&lt;sup id="fnref:3"&gt;&lt;a class="footnote-ref" href="#fn:3"&gt;3&lt;/a&gt;&lt;/sup&gt; First place went to the team from GrubHub, who made several contributions &lt;em&gt;including&lt;/em&gt; implementing metrics@N.&lt;/p&gt;
&lt;div class="image"&gt;
&lt;img src="https://danturkel.com/2023/10/31/evalrs.jpg" alt="Jacopo Tagliabue presenting the introduction to the EvalRS workshop at KDD 2023."&gt;
&lt;p&gt;Jacopo holding court at the EvalRS workshop, via &lt;a href="https://photos.app.goo.gl/xs8NkTxkxgQEiDxk9"&gt;Mattia Pavoni&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;Joey Robinson wrapped up the workshop with the second keynote, Ask and Answer: A Case Study in ML Evaluation at Snap. I really liked his central thesis, which is that a model evaluation framework has to be able to mode than just tell us &amp;ldquo;is this model good or bad?&amp;rdquo;—it has to provide a means for us to &lt;em&gt;ask and answer questions&lt;/em&gt; about how model&amp;rsquo;s behavior.&lt;/p&gt;
&lt;p&gt;I want to give a huge thank you to the EvalRS organizers for putting together a great workshop (and afterparty), with an extra special thanks to Jacopo Tagliabue, who&amp;rsquo;s always been generous with his time and insights on all things RecSys and ML.&lt;/p&gt;
&lt;h3&gt;Joint Optimization of Ranking and Calibration with Contextualized Hybrid Model&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Xiang-Rong Sheng et al, Alibaba. Paper at &lt;a href="https://dl.acm.org/doi/abs/10.1145/3580305.3599851"&gt;ACM&lt;/a&gt; and &lt;a href="https://arxiv.org/abs/2208.06164"&gt;arXiv&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Many recommender systems have moved from estimating user ratings or click probabilities to focusing on learning relative rankings between items, since a ranked list is often the user-facing output of the system. However, a well-calibrated estimate of click-through rate is often still desirable (especially in online advertising contexts), and ranking loss functions (which typically pass logits for each item through a softmax to measure &lt;em&gt;relative&lt;/em&gt; importance) aren&amp;rsquo;t well-suited to well-calibrated point estimates of probabilities. The authors of this work want to have their ranking performance cake and eat their click rate estimates too.&lt;/p&gt;
&lt;p&gt;The go-to solution for tasks with competing loss functions is to just optimize for some weighted sum of the two losses, but the authors note that &lt;a href="https://dl.acm.org/doi/10.1145/2783258.2788582"&gt;it&amp;rsquo;s been done&lt;/a&gt; and still fails to produce interpretable probabilities. Their solution is a bit more complex, but also clever. Rather than producing one logit per item, they produce two: one representing a click and one representing a non-click. The probability estimate is the sigmoid of the difference between the click and non-click logits (equivalent to the softmax of the two logits) and its calibration is optimized with log loss. Simultaneously, the ranking performance is optimized with contrastive loss encouraging a high logit for the positive sample and a log logit for the negative samples within a session (and there may be multiple sessions within a batch). The final loss function is a convex sum of these two.&lt;/p&gt;
&lt;div class="image"&gt;
&lt;img src="https://danturkel.com/2023/10/31/jrc.png" alt="A diagram contrasting different ways of calculating loss for ranking and probability estimation."&gt;
&lt;p&gt;A figure from the paper comparing pointwise loss, listwise loss, and the joint ranking and calibration loss.&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;It&amp;rsquo;s a nice trick, and separating out negative examples by session within the batch is something I hadn&amp;rsquo;t seen before (and required some work to ensure that entire sessions would be placed together in a batch during distributed training). The authors evaluated it against pointwise, pair/listwise, and convex sum losses on two open source datasets and their own logged data from production and found it (typically) outperformed the baselines on both ranking and calibration metrics. More importantly, the model was evaluated in a user-level A/B test against a pointwise baseline and brought a 4.4% lift in CTR, 2.4% increase in revenue per thousand impressions, and a 0.27% drop in log loss.&lt;/p&gt;
&lt;h3&gt;Recommender quick hits&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dl.acm.org/doi/10.1145/3580305.3599386"&gt;Impatient Bandits: Optimizing Recommendations for the Long-Term Without Delay&lt;/a&gt; by Thomas McDonald et al, Spotify and University of Manchester. When recommending podcasts, Spotify found it was more valuable to model &lt;em&gt;long-term&lt;/em&gt; engagement outcomes over short-term ones and &lt;a href="https://arxiv.org/abs/2302.03561"&gt;developed a model&lt;/a&gt; for just that purpose. However, collecting data on long-term success of podcasts doesn&amp;rsquo;t work when you want to recommend &lt;em&gt;new&lt;/em&gt; content. This work adapts the task of recommending podcasts for long-term engagement to the cold-start bandit setting. Using a short-term engagement proxy is not sufficient, but a continuously updated estimate of long-term engagement helps the bandit converge speedily on long-term optimal recommendations. Code available &lt;a href="https://github.com/spotify-research/impatient-bandits"&gt;on Github&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dl.acm.org/doi/10.1145/3580305.3599796"&gt;Controllable Multi-Objective Re-ranking with Policy Hypernetworks&lt;/a&gt; by Sirui Chen et al, Renmin University and Alibaba (code on &lt;a href="https://github.com/lyingCS/Controllable-Multi-Objective-Reranking"&gt;Github&lt;/a&gt;). One of many papers on multi-task recommendation, this one aims to improve on the fact that a weighting of different objectives is often chosen during or before training time and cannot easily be changed without re-training. Their contribution is a re-ranking model which leverages a hypernetwork that can adapt the model to a new set of objective weights on the fly, allowing the same model to be used across multiple surfaces where it may have slightly different use-cases.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dl.acm.org/doi/abs/10.1145/3580305.3599826"&gt;Fresh Content Needs More Attention: Multi-funnel Fresh Content Recommendation&lt;/a&gt; by Jianling Wang et al, Google. At the core of this paper is a fairly obvious take-away for recommendation UX: it&amp;rsquo;s beneficial to set aside a slot to surface new content in order to learn more about it. However, there are modeling and engineering insights here as well, like omitting item ID embeddings from a network in order to encourage better generalization and avoid cold start problems, and designing a &amp;ldquo;user corpus co-diverted experiment&amp;rdquo; which splits both users and items in order to prevent leakage between treatment groups.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dl.acm.org/doi/10.1145/3580305.3599519"&gt;Text is All You Need: Learning Language Representations for Sequential Recommendation&lt;/a&gt; by Jiacheng Li et al, UCSD and Amazon. This work presents Recformer, a sequential recommender model which encodes items as textual descriptions based on their metadata. The work doesn&amp;rsquo;t explicitly leverage the brand-name foundation LLMs we all love to talk about these days (though &lt;a href="https://blog.reachsumit.com/posts/2023/05/tuning-llm-for-recsys/"&gt;there are people doing that work&lt;/a&gt;) and instead adapts a bidirectional transformer architecture not unlike that of &lt;a href="https://arxiv.org/abs/2004.05150"&gt;Longformer&lt;/a&gt;. The authors talk about how recommenders which rely on embeddings of item IDs lose the ability to generalize or transfer since representations linked to IDs can&amp;rsquo;t easily be shared from item to item, but I am skeptical that we need to entirely abandon item IDs.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dl.acm.org/doi/abs/10.1145/3580305.3599892"&gt;RankFormer: Listwise Learning-to-Rank using Listwide Labels&lt;/a&gt; by Maarten Buyl et al, Amazon. Read that title slowly: we&amp;rsquo;re talking about list-&lt;em&gt;wise&lt;/em&gt; loss (measurements of ranking that consider every element in the ranked list) and list-&lt;em&gt;wide&lt;/em&gt; signal (a label indicating whether the user received value from the entire list of recommendations). The former has existed for a while as an extension of pairwise loss functions for ranking, but the notion of listwide signal is fairly novel and comes into play particularly in cases where the user didn&amp;rsquo;t click &lt;em&gt;any&lt;/em&gt; items in a list. Existing ranking metrics cannot learn from the case where a user doesn&amp;rsquo;t interact with any of the returned results, but the authors argue that this situation is both very frequent and a valuable source of user data. Thus the incorporation of this listwide signal allows them to augment more traditional LTR methods—indeed, their best performance was with a blend of listwise and listwide loss, rather than just one or the other.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Search&lt;/h2&gt;
&lt;p&gt;Search is intrinsically tied to recommendation and increasingly personalization, so I&amp;rsquo;ve been taking more and more of an interest in the topic.&lt;sup id="fnref:4"&gt;&lt;a class="footnote-ref" href="#fn:4"&gt;4&lt;/a&gt;&lt;/sup&gt; As large language models have blown up, search has seen a surge of interest, with particular focus on semantic search methods (which operate on embeddings rather than on text itself) and hybrid methods for tying semantic and more &amp;ldquo;classical&amp;rdquo; approaches.&lt;/p&gt;
&lt;h3&gt;Optimizing Airbnb Search Journey with Multi-task Learning&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Chun How Tan et al, Airbnb. Paper at &lt;a href="https://dl.acm.org/doi/10.1145/3580305.3599881"&gt;ACM&lt;/a&gt; and &lt;a href="https://arxiv.org/abs/2305.18431"&gt;arXiv&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Just like in the Spotify Impatient Bandits paper mentioned above, Airbnb deals with optimizing outcomes with substantial delays between user actions and measured rewards—in this case, search journeys which may take weeks before a final reservation is made. Their method, Journey Ranker, is a multi-task model that leverages intermediate milestones to improve search personalization along this journey.&lt;/p&gt;
&lt;p&gt;Airbnb searchers often abandon search sessions before ultimately making reservations later down the line, so clicks on search results are really only the first of many steps that must occur before an actual booking. Journey Ranker considers not only clicks but payment page visits, booking requests, &lt;em&gt;host acceptance&lt;/em&gt; of booking request, and whether or not either the host or booker cancels the booking.&lt;/p&gt;
&lt;p&gt;A shared representation embeds both the listing and search context, while the &amp;ldquo;base module&amp;rdquo; leverages this representation to make several intermediate probability estimates (probability of click given impression, booking given click, etc.) whose product is the final desired probability, as inspired by the &lt;a href="https://arxiv.org/abs/1804.07931"&gt;Entire Space Multi-Task Model&lt;/a&gt; approach. A &amp;ldquo;Twiddler&amp;rdquo; module scores listings based on negative milestones like cancellation or booking rejection, using the same shared representation. Lastly, a combination module produces a learned linear combination the base and Twiddler module outputs.&lt;/p&gt;
&lt;div class="image"&gt;
&lt;img src="https://danturkel.com/2023/10/31/journey-ranker.png" alt="A diagram of the paper's combined architecture."&gt;
&lt;p&gt;A figure from the paper visualizing the Journey Ranker combination module.&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;The paper goes into detail on &lt;a href="https://increment.com/documentation/notes-on-the-synthesis-of-labyrinths/"&gt;design decisions taken and not taken&lt;/a&gt;, and demonstrates the interpretability of the combination module&amp;rsquo;s linear task weights. They evaluated the method in offline and online settings, with the online test leading to a 0.61% increase in uncancelled bookers. They also applied the same method to other Airbnb use cases, netting a 9.0% increase in uncancelled bookers for online experiences (a newer and less optimized flow) and a 3.7% reduction in email unsubscribes.&lt;/p&gt;
&lt;h3&gt;End-to-End Query Term Weighting&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Karan Samel et al, Google. Paper at &lt;a href="https://dl.acm.org/doi/10.1145/3580305.3599815"&gt;ACM&lt;/a&gt; and &lt;a href="https://research.google/pubs/pub52462/"&gt;Google Research&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;As sexy as &amp;ldquo;semantic search&amp;rdquo; may be, deep learning models are expensive to run at massive scale. As a result, bag-of-words and n-gram methods are often still deployed, with tokens weighted by scoring functions like &lt;a href="https://en.wikipedia.org/wiki/Okapi_BM25"&gt;BM25&lt;/a&gt;, which has proven to be a formidable baseline.&lt;/p&gt;
&lt;p&gt;The authors of this paper wanted to adapt newer deep learning methods to operate within the bounds of a bag-of-words system by using a language model to learn weights (end-to-end) for query terms that are used as part of the input to the BM25 scoring function.&lt;/p&gt;
&lt;p&gt;A masked language modeling task is used for pre-training an open source BERT checkpoint. Then a second pre-training task is used to learn uniform term weights as a starting point for the actual term weighting tasks. The fine-tuning is the end-to-end step which leverages the entire information retrieval pipeline: the query is fed to the BERT model to produce term weights, which are combined with document statistics as input to BM25. A combination of pointwise and ranking loss is then back-propagated all the way back to the BERT model to update the term weights.&lt;/p&gt;
&lt;div class="image"&gt;
&lt;img src="https://danturkel.com/2023/10/31/query-weighting.png" alt="A diagram of the paper's query term weighting system."&gt;
&lt;p&gt;A figure from the paper visualizing the architecture of the end-to-end weighting system.&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;There&amp;rsquo;s other fancy bits in here, like using a &lt;a href="https://huggingface.co/docs/transformers/model_doc/t5"&gt;T5 model&lt;/a&gt; to generate soft labels for negative examples as well as a query expansion solution which weights both the original query terms as well as expanded terms. Another challenge was reconciling the wordpieces from BERT&amp;rsquo;s tokenizer with the world-level terms used by the existing system, which required additional masking and pooling.&lt;/p&gt;
&lt;p&gt;The model performs well on the evaluation datasets, with some tough competition from &lt;a href="https://github.com/naver/splade"&gt;SPLADE&lt;/a&gt;. They don&amp;rsquo;t mention any online experiments, but note that since their model only incurs a single forward pass on BERT, it&amp;rsquo;s &amp;ldquo;tractable to perform during serving.&amp;rdquo; Nonetheless, I admire the pragmatism underlying the approach. &lt;em&gt;Replacing&lt;/em&gt; the existing retrieval system with deep learning would be a massive infrastructural change with integration costs, not to mention monetary and performance costs, but &lt;em&gt;leveraging&lt;/em&gt; a deep learning model within the existing system can potentially get the best of both worlds.&lt;/p&gt;
&lt;h3&gt;Search quick hit&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dl.acm.org/doi/abs/10.1145/3580305.3599927"&gt;UnifieR: A Unified Retriever for Large-Scale Retriever&lt;/a&gt; by Tao Shen et al, University of Technology Sydney, Microsoft, and Ohio State University. This paper was presented right after End-to-End Query Term Weighting and practically threw down the gauntlet by claiming that methods which use a non-learnable function like BM25 to combine dense and sparse representations cannot possibly provide rich enough interaction between those representations. UnifieR learns both dense and sparse representations which share global context and supports both dense and sparse retrieval, as well as a select-then-re-rank &amp;ldquo;uni-retrieval&amp;rdquo; paradigm.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Software and engineering challenges&lt;/h2&gt;
&lt;p&gt;These papers focused less on novel algorithms (though they do include some) and more on building new solutions to improve performance and resilience. Software engineering is a fairly rare topic at ML conferences, but when it does come up it often provides a unique look behind the curtain at engineering challenges faced by some of the biggest tech companies on the planet.&lt;/p&gt;
&lt;h3&gt;Yggdrasil Decision Forests&lt;/h3&gt;
&lt;div class="image small"&gt;
&lt;img src="https://danturkel.com/2023/10/31/yggdrasil-logo.png" alt="The logo for the Yggdrasil decision forests library."&gt;
&lt;/div&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://twitter.com/mat_gb"&gt;Matthieu Guillaume-Bert&lt;/a&gt; et al, Google and Pinecone. Paper at &lt;a href="https://dl.acm.org/doi/10.1145/3580305.3599933"&gt;ACM&lt;/a&gt; and &lt;a href="https://arxiv.org/abs/2212.02934"&gt;arXiv&lt;/a&gt;,  code on &lt;a href="https://github.com/google/yggdrasil-decision-forests"&gt;Github&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Yggdrasil is a new decision forest library in C++ and Python (as &lt;a href="https://github.com/tensorflow/decision-forests"&gt;TensorFlow Decision Forests&lt;/a&gt;), with support for inference in Go and JavaScript.&lt;/p&gt;
&lt;p&gt;I have a thing for talks about new libraries, like &lt;a href="https://github.com/NVIDIA-Merlin/Transformers4Rec"&gt;Transformers4Rec&lt;/a&gt; at &lt;a href="https://danturkel.com/2021/12/06/belated-highlights-from-recsys-2021.html"&gt;RecSys 2021&lt;/a&gt; or &lt;a href="https://dl.acm.org/doi/abs/10.1145/3523227.3547387"&gt;TorchRec&lt;/a&gt; at RecSys 2022.&lt;sup id="fnref:5"&gt;&lt;a class="footnote-ref" href="#fn:5"&gt;5&lt;/a&gt;&lt;/sup&gt; But what I really loved about the Yggdrasil talk wasn&amp;rsquo;t the discussion of features (a bazillion different tree-based models, evaluation methods, distributed training, etc.) but the focus on the &lt;em&gt;design principles&lt;/em&gt; that went into making the library:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Simplicity of use: helpful interactions and messages at the appropriate level of abstraction, sensible defaults, clarity and transparency&lt;/li&gt;
&lt;li&gt;Safety of use: warnings and errors that make it hard for users to make mistakes, easily-accessible best practices&lt;/li&gt;
&lt;li&gt;Modularity and high-level abstraction: sufficiently complex pieces of code should be understandable independently, with well-defined interfaces between them&lt;/li&gt;
&lt;li&gt;Integration with other ML libraries: avoid limiting users to methods available within their chosen library, favor composability&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It reminds me of &lt;a href="https://github.com/vim/vim/blob/531da5955e03afadb2f0cf72264fe8deb4bf430e/runtime/doc/develop.txt#L25"&gt;Vim&amp;rsquo;s design principles&lt;/a&gt;, and re-affirms my belief (partially inspired &lt;a href="https://hinge.co/mission"&gt;by my employer&lt;/a&gt;) that having principles from the outset is a good way to guide decision-making during any large project.&lt;/p&gt;
&lt;div class="image"&gt;
&lt;img src="https://danturkel.com/2023/10/31/yggdrasil-error.png" alt="A comparison of an opaque and a more verbose error message."&gt;
&lt;p&gt;An excerpt from the Yggdrasil paper showing the library's approach to helpful, humane errors.&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;As you can see in the example from the paper shown above, these principles manifest in a library that attempts to guide you to use it correctly and effectively. The authors note that machine learning systems are often at risk of running without error, while silently doing something totally wrong, so this approach is valuable in preventing some of those potentially insidious errors.&lt;/p&gt;
&lt;h3&gt;Revisiting Neural Retrieval on Accelerators&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Jiaqi Zhai et al, Meta. Paper at &lt;a href="https://dl.acm.org/doi/10.1145/3580305.3599897"&gt;ACM&lt;/a&gt; and &lt;a href="https://arxiv.org/abs/2306.04039"&gt;arXiv&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Deep learning has gained popularity in recommendation, older methods like matrix factorization have proven to be a hard baseline to beat.&lt;sup id="fnref:6"&gt;&lt;a class="footnote-ref" href="#fn:6"&gt;6&lt;/a&gt;&lt;/sup&gt; But even as &lt;a href="https://www.semanticscholar.org/paper/Modeling-relationships-at-multiple-scales-to-of-Bell-Koren/cae130eb00f79a415b4ba2f01dbb7019c4a4666d"&gt;matrix factorization&lt;/a&gt; has been largely supplanted neural alternatives, it has survived in the fundamental structure of most dense retrieval models: representing similarity as the dot product of learned vectors for users (or queries) and items. By saving all the vectors to an index, retrieval can be done with approximate nearest neighbors methods which scale to massive datasets while remaining highly performant.&lt;/p&gt;
&lt;p&gt;But dot products just aren&amp;rsquo;t good enough for Zhai et al. They write: &amp;ldquo;The relationship between users and items in real world, however, demonstrates a significant level of complexity, and may not
be approximated well by dot products,&amp;rdquo; citing the fact that interaction likelihoods are often frequently much higher-rank than these low-rank approximations can model, and noting that ranking models have mostly moved forward to more complex neural architectures. Nonetheless, there haven&amp;rsquo;t yet been solid alternatives to the maximum-inner-product search formulation. The authors seek to change this, and their contribution is threefold: a &amp;ldquo;mixture of logits&amp;rdquo; similarity function designed to outperform dot products and generalize better to the long tail, a hierarchical retrieval strategy that employs GPUs and TPUs to scale this method to corpora of hundreds of millions of items, and experiments demonstrating improvements on baseline datasets and Meta production traffic.&lt;/p&gt;
&lt;p&gt;The authors go on to discuss not only their novel similarity function but the various tricks they employ to make it run efficiently on the GPU, bringing to mind a similar blend of algorithmic and engineering wizardry in &lt;a href="https://arxiv.org/abs/1906.00091"&gt;Meta&amp;rsquo;s DLRM paper&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Improving Training Stability for Multitask Ranking Models in Recommender Systems&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Jiaxi Tang et al, Google and Deepmind. Paper at &lt;a href="https://dl.acm.org/doi/10.1145/3580305.3599846"&gt;ACM&lt;/a&gt; and &lt;a href="https://arxiv.org/abs/2302.09178"&gt;arXiv&lt;/a&gt;, code on &lt;a href="https://github.com/tensorflow/recommenders/blob/main/tensorflow_recommenders/experimental/optimizers/clippy_adagrad.py"&gt;Github&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I mentioned that scale and multitask recommenders were major themes at KDD this year, and this paper (which was the winner on the &lt;a href="https://kdd.org/kdd2023/awards/"&gt;Best Paper Award for Applied Data Science&lt;/a&gt;) has both. This paper is all about techniques to prevent instability (loss divergence) while training a multitask recommender for YouTube.&lt;/p&gt;
&lt;p&gt;Training failures had clearly become a major issue for the team, and they found it hard to reproduce (models didn&amp;rsquo;t always diverge, even with the same configuration), hard to detect (divergence might occur before metrics are logged), and hard to measure. Rolling back to earlier training checkpoints didn&amp;rsquo;t address the heart of the problem and wasn&amp;rsquo;t even guaranteed to work.&lt;/p&gt;
&lt;div class="image"&gt;
&lt;img src="https://danturkel.com/2023/10/31/loss-divergence.png" alt="A line plot demonstrating loss divergence of a deep learning model."&gt;
&lt;p&gt;A (cropped) figure from the paper demonstrating temporary and permanent loss divergence.&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;They found that the root cause of loss divergence was &amp;ldquo;step size being too large when loss curvature is steep&amp;rdquo; (they even bolded it in their paper). Recommendation models are particularly susceptible because they&amp;rsquo;re trained on many features in sequential data, which means there&amp;rsquo;s a very large probability of distribution shift during training, which in turn requires a consistently large learning rate to continue adapting to these shifts. Furthermore, ranking models diverged more than retrieval models seemingly due to their larger size and their tendency to be multitask (big gradients for one task can spoil performance on others through shared layers).&lt;/p&gt;
&lt;p&gt;The solution ends up being a fancy variant of gradient clipping, affectionately dubbed &amp;ldquo;Clippy.&amp;rdquo; What makes Clippy unique is that it controls the size of the model update rather than the size of the gradient itself—these two values are synonymous in SGD, but can be very different with more sophisticated optimizers. They also measure the size of the update using the L-infinity norm, which is sensitive to large updates in even just a few coordinates.&lt;/p&gt;
&lt;h3&gt;Engineering quick hit&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dl.acm.org/doi/10.1145/3580305.3599416"&gt;LightToken: A Task and Model-agnostic Lightweight Token Embedding Framework for Pre-trained Language Models&lt;/a&gt; by Haoyu Wang et al, Amazon and Purdue University. Everyone using language models likely needs to store and access a bunch of embeddings, but this can be challenging for deployment on edge devices. LightToken is a new embedding compression method which claims the state of the art in shrinking embedding tables. The four-step approach can be applied to any model backbone without the need for retraining while preserving model performance.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Grab bag&lt;/h2&gt;
&lt;p&gt;Not every paper I liked fits neatly into the themes above. Below are a few more papers of note that I wanted to highlight:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dl.acm.org/doi/10.1145/3580305.3599928"&gt;Variance Reduction Using In-Experiment Data&lt;/a&gt; by Alex Deng et al, Airbnb. Variance reduction methods increase the power of controlled experiments and allow experimenters to detect smaller effects. The work is a conceptual evolution of &lt;a href="https://www.exp-platform.com/Documents/2013-02-CUPED-ImprovingSensitivityOfControlledExperiments.pdf"&gt;CUPED&lt;/a&gt;, which leverages &lt;em&gt;pre&lt;/em&gt;-experiment covariates for variance reduction. This paper instead uses &lt;em&gt;in&lt;/em&gt;-experiment data, particularly leading indicator metrics which estimate the desired (but delayed) ultimate outcome. The idea is similar at a high level to the Spotify &amp;ldquo;impatient bandit&amp;rdquo; mentioned earlier: using intermediate estimates of delayed outcomes.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dl.acm.org/doi/10.1145/3580305.3599823"&gt;Fair Multilingual Vandalism Detection System for Wikipedia&lt;/a&gt; by Mykola Trokhymovych et al, Wikimedia (code on &lt;a href="https://github.com/trokhymovych/KI_multilingual_training"&gt;Github&lt;/a&gt;). Wikipedia wanted to improve vandalism detection across languages, while preventing undue bias against anonymous editors who might become future repeat contributors.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dl.acm.org/doi/10.1145/3580305.3599925"&gt;Uncertainty-Aware Probabilistic Travel Time Prediction for On-Demand Ride-Hailing at DiDi&lt;/a&gt; by Hao Liu et al, Didi. The authors outline how travel time prediction has evolved at the Chinese ride-hailing app, starting with gradient boosted trees, evolving to RNNs, and then to graph-based methods. But all of these techniques estimated time as a single numerical output and failed to account for potential uncertainty. Their new model estimates a travel time &lt;em&gt;distribution&lt;/em&gt; instead, reformulating the problem as multiclass classification, where the labels are discretized bins of travel times.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dl.acm.org/doi/10.1145/3580305.3599822"&gt;Extreme Multi-Label Classification for Ad Targeting using Factorization Machines&lt;/a&gt; by Martin Pavlovski et al, Yahoo and eBay. I have a big soft spot for &lt;a href="https://scholar.google.com/citations?view_op=view_citation&amp;amp;hl=en&amp;amp;citation_for_view=yR-ugIoAAAAJ:Tyk-4Ss8FVUC"&gt;factorization machines&lt;/a&gt;, a non-neural recommendation model that adds metadata features to classical collaborative filtering and which has stood as a strong baseline against (not to mention &lt;a href="https://arxiv.org/abs/1703.04247"&gt;inspiration&lt;/a&gt; for) much fancier methods. In this work, the authors leverage a multi-label variant of FMs for ad targeting in a setting where neural methods produce an unacceptable amount of latency. Their dataset had 200,000 features, 1098 labels, and the model had only marginal memory costs versus a linear baseline, with inference in 0.16 milliseconds.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.google.com/presentation/d/1Cky9sB_flLirZKdWq6Ahbx1mEQzN58i5MZ6Dc3OgRik/edit#slide=id.g16197112905_0_0"&gt;New Paradigms in the Large Language Model Renaissance&lt;/a&gt; by Jason Wei, OpenAI. I&amp;rsquo;ve got to share at least one LLM talk from the conference, and my favorite was Jason&amp;rsquo;s, which discussed the ways that LLMs have led to changes in the ways ML research happens. The massive scale needed for LLMs means individual researchers can&amp;rsquo;t easily to bottom-up ideation because they need a team with specialized skills, infrastructure, and resources to test out ideas. Emergent abilities of LLMs mean that researchers constantly need new benchmarks and evaluation methods, but also mean that new use cases don&amp;rsquo;t always necessitate new models. Lastly, advances in so-called LLM &amp;ldquo;reasoning&amp;rdquo; with methods like &lt;a href="https://arxiv.org/abs/2201.11903"&gt;chain-of-thought&lt;/a&gt; allow models to handle bigger, more complex problems which are specified in natural language. &lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;There are around 20 papers in this write-up, and I left plenty of cool talks on the cutting room floor too. I&amp;rsquo;m thankful for my employer for sending me to conferences like KDD and &lt;a href="https://danturkel.com/2021/12/06/belated-highlights-from-recsys-2021.html"&gt;Recsys&lt;/a&gt; where I&amp;rsquo;m able to hear so many ideas and take away so much. If you think I missed a particularly cool paper or talk, or just want to share your thoughts, don&amp;rsquo;t hesitate to &lt;a href="mailto:daturkel@gmail.com"&gt;reach out&lt;/a&gt;, and thanks for reading &amp;lsquo;til the end! If you liked this post and want to hear more from me in the future, you can follow my &lt;a href="https://danturkel.com/feeds/rss.xml"&gt;RSS feed&lt;/a&gt;.&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;I&amp;rsquo;ll note that this selection will be pretty biased by what I chose to attend. For instance, KDD features a &lt;em&gt;ton&lt;/em&gt; of content on graph learning, the mast majority of which I didn&amp;rsquo;t attend because I don&amp;rsquo;t do much work with graph data. On the other hand, you&amp;rsquo;ll find recommender systems and search over-represented here, as those are areas of particular interest to me. I also missed the some workshops that I would&amp;rsquo;ve loved to have seen, like &lt;a href="https://sites.google.com/view/kdd23onlinemarketplaces/home"&gt;Decision Intelligence and Analytics for Online Marketplaces&lt;/a&gt; and &lt;a href="https://oars-workshop.github.io/"&gt;Online and Adaptive Recommender Systems&lt;/a&gt;.&amp;#160;&lt;a class="footnote-backref" href="#fnref:1" title="Jump back to footnote 1 in the text"&gt;&amp;uarr;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;It turns out that personalizing the metric can be quite harsh: the baseline model had a hit rate at 100 of 4.8% but a hit rate at median listens/day of only 0.03%!&amp;#160;&lt;a class="footnote-backref" href="#fnref:2" title="Jump back to footnote 2 in the text"&gt;&amp;uarr;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;Second place&amp;hellip;though I&amp;rsquo;m not sure if more than two teams submitted 😅.&amp;#160;&lt;a class="footnote-backref" href="#fnref:3" title="Jump back to footnote 3 in the text"&gt;&amp;uarr;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;For others interested in search basics, I&amp;rsquo;d highly recommend the &lt;a href="https://uplimit.com/course/search-fundamentals"&gt;Search Fundamentals course from Uplimit&lt;/a&gt;, taught by Grant Ingersoll and Daniel Tunkelang. It&amp;rsquo;s a great intro into key search concepts and a whirlwind tour of basic Elasticsearch/OpenSearch functionality. I took the course in February and it opened my eyes to a lot of the parallels between search and recommendation.&amp;#160;&lt;a class="footnote-backref" href="#fnref:4" title="Jump back to footnote 4 in the text"&gt;&amp;uarr;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:5"&gt;
&lt;p&gt;I never did get around to writing that highlights of RecSys &amp;lsquo;22 post&amp;hellip;&amp;#160;&lt;a class="footnote-backref" href="#fnref:5" title="Jump back to footnote 5 in the text"&gt;&amp;uarr;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:6"&gt;
&lt;p&gt;For example, see the work of Steffen Rendle, who has contributed &lt;a href="https://scholar.google.com/citations?view_op=view_citation&amp;amp;hl=en&amp;amp;citation_for_view=yR-ugIoAAAAJ:Tyk-4Ss8FVUC"&gt;factorization machines&lt;/a&gt; and &lt;a href="https://scholar.google.com/citations?view_op=view_citation&amp;amp;hl=en&amp;amp;citation_for_view=yR-ugIoAAAAJ:d1gkVwhDpl0C"&gt;Bayesian personalized ranking&lt;/a&gt; to the &lt;em&gt;pre-neural&lt;/em&gt; era of recsys. He has continued to demonstrate the performance of non-neural recommendation models &lt;a href="https://arxiv.org/abs/2005.09683"&gt;several&lt;/a&gt; &lt;a href="https://arxiv.org/abs/2110.14037"&gt;times&lt;/a&gt;. See also &lt;a href="https://dl.acm.org/doi/abs/10.1145/3460231.3475944"&gt;Are We Really Making Much Progress?&lt;/a&gt; and &lt;a href="https://dl.acm.org/doi/abs/10.1145/3460231.3475944"&gt;Reenvisioning the comparison between Neural Collaborative Filtering and Matrix Factorization&lt;/a&gt;.&amp;#160;&lt;a class="footnote-backref" href="#fnref:6" title="Jump back to footnote 6 in the text"&gt;&amp;uarr;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content><category term="misc"></category><category term="conference"></category><category term="machine-learning"></category></entry><entry><title>Math and code for Are You the One?</title><link href="https://danturkel.com/2023/01/25/math-code-are-you-the-one.html" rel="alternate"></link><published>2023-01-25T00:00:00-05:00</published><updated>2023-01-25T00:00:00-05:00</updated><author><name>Dan Turkel</name></author><id>tag:danturkel.com,2023-01-25:/2023/01/25/math-code-are-you-the-one.html</id><summary type="html">&lt;p&gt;I have an unusual fondness for the reality TV dating show Are You the One? What appeals to me is not (just) the drama and drunken antics of watching a bunch of twenty-somethings compete for love and screen-time. What I really like is the math.&lt;/p&gt;</summary><content type="html">&lt;div class="image"&gt;
&lt;img src="https://danturkel.com/2023/01/25/a-lovers-tryst-ludek-marold.jpg" alt="A pen and gouache painting of a man and woman having a tryst in some greenery."&gt;
&lt;p&gt;&lt;em&gt;A Lover's Tryst&lt;/em&gt; (undated, 19th century) by Luděk Marold, via &lt;a href="https://www.christies.com/en/lot/lot-ludwig-marold-czech-1865-1898-5694275/"&gt;Christie’s&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;I have an unusual fondness for the reality TV dating show &lt;a href="https://en.wikipedia.org/wiki/Are_You_the_One%3F"&gt;&lt;em&gt;Are You the One?&lt;/em&gt;&lt;/a&gt; What appeals to me is not (just) the drama and drunken antics of watching a bunch of twenty-somethings compete for love and screen-time. What I really like is the &lt;em&gt;math&lt;/em&gt;.&lt;/p&gt;
&lt;h3&gt;The math&lt;/h3&gt;
&lt;p&gt;The math on &lt;em&gt;AYTO&lt;/em&gt; derives from its premise: 11 men and 11 women have been paired up by mysterious matchmakers into 11 &amp;ldquo;perfect matches&amp;rdquo; and the contestants need to identify their perfect match for a chance at love and (their share of) 1 million dollars (split 22 ways). The group has two ways of finding information to determine all 11 couples. The first is the straightforward &amp;ldquo;Truth Booth,&amp;rdquo; where a chosen couple gets to find out definitively whether or not they are a match. The second is the &amp;ldquo;Matchup Ceremony,&amp;rdquo; where the group pairs up into their guess at the 11 couples and finds out &lt;em&gt;how many&lt;/em&gt; couples are correct, &lt;em&gt;but not which ones&lt;/em&gt;, a bit like the game &lt;a href="https://en.wikipedia.org/wiki/Mastermind_(board_game)"&gt;MasterMind&lt;/a&gt; or even Wordle.&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt; Each episode typically has one Truth Booth and one Matchup Ceremony.&lt;/p&gt;
&lt;p&gt;With 11 men and 11 women paired into 11 heterosexual couples, there are a total of &lt;a href="https://en.wikipedia.org/wiki/Factorial"&gt;11!&lt;/a&gt; (or about 40 million) possible pairings.&lt;sup id="fnref:2"&gt;&lt;a class="footnote-ref" href="#fn:2"&gt;2&lt;/a&gt;&lt;/sup&gt; Every bit of information winnows the set of possible pairings down a bit. For instance, if a lucky Truth Booth on the first episode confirms a couple, then there would only be 10! = 3.6 million pairings left—an impressive 91% reduction. If that same Truth Booth revealed that the couple &lt;em&gt;wasn&amp;rsquo;t&lt;/em&gt; a match, then there would be 10 × 10! = 36 million pairings left, a measly 9% reduction.&lt;/p&gt;
&lt;p&gt;The math behind figuring out the impact of one Truth Booth on the first episode isn&amp;rsquo;t so bad, but it gets a bit tricky after that. And if you&amp;rsquo;re imagining the contestants seated around a table working through the possibilities, well&amp;hellip;it&amp;rsquo;s not exactly &lt;a href="https://www.youtube.com/watch?v=ROOphvSpEzA"&gt;that kind of show&lt;/a&gt;. The contestants &lt;a href="https://www.reddit.com/r/IAmA/comments/2l7kxy/im_ryan_devlin_host_of_are_you_the_one_on_mtv_ama/cls8nqx/"&gt;aren&amp;rsquo;t even allowed pen and paper&lt;/a&gt;, much less computers. Instead, they&amp;rsquo;re provided with the slightly less useful tool of seemingly unlimited amounts of alcohol.&lt;/p&gt;
&lt;p&gt;Nevertheless, they almost always succeed in figuring it all out by the end of the season.&lt;/p&gt;
&lt;div class="youtube-container"&gt;
  &lt;div class="youtube"&gt;
  &lt;iframe src="https://www.youtube-nocookie.com/embed/iD1vQRfmZtQ" frameborder='0' allowfullscreen&gt;&lt;/iframe&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;h3&gt;The code&lt;/h3&gt;
&lt;p&gt;A fan favorite way to track the group&amp;rsquo;s progress throughout the season is with a spreadsheet, like &lt;a href="https://docs.google.com/spreadsheets/d/1MQj2c9TGzYeq3PAnWo6ZlOGRDnBrbWd3yxDnFjXX_wM/edit?usp=sharing"&gt;this one&lt;/a&gt; I made for season 4 in 2017 (check out the different tabs to see the impact of progressively more information). With some thorough book-keeping, you can deduce perfect matches and sometimes even solve the entire thing before the season is over. It&amp;rsquo;s fun, but it&amp;rsquo;s also a bit tedious.&lt;sup id="fnref:3"&gt;&lt;a class="footnote-ref" href="#fn:3"&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;In 2017, when Season 5 was airing, I made &lt;a href="https://github.com/daturkel/ayto-calculator"&gt;ayto-calculator&lt;/a&gt;, a &lt;a href="https://github.com/daturkel/ayto-calculator/blob/master/rut1.py"&gt;script&lt;/a&gt; that tracked couple probabilities as information came in. In 2019, I made &lt;a href="https://github.com/daturkel/pyto"&gt;pyto&lt;/a&gt; to handle Season 8&amp;rsquo;s unique format (see &lt;a href="#fn:2"&gt;footnote 2&lt;/a&gt;) and gave &lt;a href="https://docs.google.com/presentation/d/1Da3xq0behLoUPQBp5kV8NQE8Ay1GkTYiIx6Dm43_gAI/edit?usp=sharing"&gt;a presentation&lt;/a&gt; about it at work.&lt;sup id="fnref:4"&gt;&lt;a class="footnote-ref" href="#fn:4"&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;A couple of weeks ago I got an email from a fan of the &lt;a href="https://en.wikipedia.org/wiki/Are_You_the_One%3F_(German_TV_series)"&gt;German version&lt;/a&gt; of the show seeking help on running my code. Getting my 2017 code working again was&amp;hellip;unpleasant, so I started thinking about how I might implement this 6 years later, with &lt;a href="https://danturkel.com/2022/12/27/advent-of-code-2022.html"&gt;a lot more Python knowledge&lt;/a&gt; under my belt. A few days later when I found out that the show was coming back for &lt;a href="https://www.youtube.com/watch?v=iD1vQRfmZtQ"&gt;another season&lt;/a&gt; after a three-year hiatus, I knew I had to do a real rewrite to prepare.&lt;/p&gt;
&lt;p&gt;And so a third repo, &lt;a href="https://github.com/daturkel/ayto"&gt;ayto&lt;/a&gt;, was born. It&amp;rsquo;s available on &lt;a href="https://pypi.org/project/ayto/"&gt;PyPI&lt;/a&gt; and can be installed by anyone with &lt;code&gt;pip install ayto&lt;/code&gt;. This is what it looks like in action:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;ayto&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AYTO&lt;/span&gt;

&lt;span class="n"&gt;guys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Albert&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Bob&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Charles&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Devin&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Eli&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;girls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Faith&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Gina&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Heather&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Ingrid&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Joy&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;season&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AYTO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;guys&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;girls&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;season&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;num_scenarios&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# 120&lt;/span&gt;

&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;season&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;probabilities&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;#          Albert  Bob  Charles  Devin  Eli&lt;/span&gt;
&lt;span class="c1"&gt;# Faith       0.2  0.2      0.2    0.2  0.2&lt;/span&gt;
&lt;span class="c1"&gt;# Gina        0.2  0.2      0.2    0.2  0.2&lt;/span&gt;
&lt;span class="c1"&gt;# Heather     0.2  0.2      0.2    0.2  0.2&lt;/span&gt;
&lt;span class="c1"&gt;# Ingrid      0.2  0.2      0.2    0.2  0.2&lt;/span&gt;
&lt;span class="c1"&gt;# Joy         0.2  0.2      0.2    0.2  0.2&lt;/span&gt;

&lt;span class="c1"&gt;# Bob and Heather go to the Truth Booth and learn they&amp;#39;re not a match&lt;/span&gt;
&lt;span class="n"&gt;scenarios_remaining&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;season&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;apply_truth_booth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Bob&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Heather&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scenarios_remaining&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# 96&lt;/span&gt;

&lt;span class="c1"&gt;# A Matchup Ceremony with 2 beams of light (i.e. 2 correct couples)&lt;/span&gt;
&lt;span class="n"&gt;scenarios_remaining&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;season&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;apply_matchup_ceremony&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Albert&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Faith&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Bob&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Gina&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Charles&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Heather&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Devin&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Ingrid&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Eli&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Joy&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;beams&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scenarios_remaining&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# 17&lt;/span&gt;

&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;season&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;probabilities&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;#          Albert   Bob  Charles  Devin   Eli&lt;/span&gt;
&lt;span class="c1"&gt;# Faith      0.35  0.18     0.12   0.18  0.18&lt;/span&gt;
&lt;span class="c1"&gt;# Gina       0.12  0.47     0.18   0.12  0.12&lt;/span&gt;
&lt;span class="c1"&gt;# Heather    0.18  0.00     0.47   0.18  0.18&lt;/span&gt;
&lt;span class="c1"&gt;# Ingrid     0.18  0.18     0.12   0.35  0.18&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;All we have to do to calculate the couple probabilities is keep track of every possible scenario and eliminate those which contradict Truth Booths and Match Ceremonies. Processing Truth Booths is straightforward: if the pair is a match, we eliminate every scenario that doesn&amp;rsquo;t feature that pair; if the pair was not a match, we eliminate every pair which &lt;em&gt;does&lt;/em&gt; have that scenario. Matchup Ceremonies are only slightly more complex: we keep only the scenarios that share B couples in common with the couples from the Matchup Ceremony, where B is the number of beams of light (correct couples) from the ceremony.&lt;/p&gt;
&lt;h4&gt;The data structure&lt;/h4&gt;
&lt;p&gt;There are a few considerations for doing this efficiently, the first of which is how to store the scenarios. A simple and effective way to do this is to think of all the men as always being in the same fixed order. We can then represent a set of pairings by ordering the women such that the first woman in that ordering is paired with the first man, the second with the second, and so on. That is, if there are three men and three women, we can call the men 1, 2, and 3 and the women A, B, and C. Then a pairing of 1 with C, 2 with B, and 3 with A would be written CBA, and the 6 total possible pairings are ABC, ACB, BAC, BCA, CAB, and CBA.&lt;/p&gt;
&lt;p&gt;In reality, we use integers (not letters) to represent the women, and so the starting set of all scenarios is a NumPy array 11 integers wide and roughly 40 million rows long. At first I was generating this list with Python&amp;rsquo;s &lt;code&gt;itertools.permutations&lt;/code&gt; &lt;a href="https://docs.python.org/3/library/itertools.html#itertools.permutations"&gt;function&lt;/a&gt;, but this turned out to be rather slow, taking over 6 seconds to generate the 40 million scenarios. Luckily, Daniel Giger provides a faster NumPy permutation algorithm on &lt;a href="https://stackoverflow.com/a/71231033"&gt;Stack Overflow&lt;/a&gt; that reduces the time to about a quarter of a second.&lt;/p&gt;
&lt;p&gt;Thus the above 6 combinations of 3 men and 3 women would be stored as:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;Pruning scenarios&lt;/h4&gt;
&lt;div class="youtube-container"&gt;
  &lt;div class="youtube"&gt;
  &lt;iframe src="https://www.youtube-nocookie.com/embed/FcZaEz0teu4" frameborder='0' allowfullscreen&gt;&lt;/iframe&gt;
  &lt;/div&gt;
&lt;p&gt;Reasoning about matches plays out a little differently in the show than it does in code.&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;The second major consideration is how to prune the scenarios. My previous projects and my first iteration of ayto would all &lt;a href="https://github.com/daturkel/ayto/blob/7d3d8fd64cf37e855d6427e4f477434673b16e71/src/ayto/ayto.py#L132-L137"&gt;loop through all scenarios&lt;/a&gt; and &lt;a href="https://github.com/daturkel/ayto/blob/7d3d8fd64cf37e855d6427e4f477434673b16e71/src/ayto/ayto.py#L70-L75"&gt;loop through each pair in a scenario&lt;/a&gt;, checking to see how many pairs match between a scenario and a Truth Booth or Matchup Ceremony. This is very slow.&lt;/p&gt;
&lt;p&gt;I realized that a much better way to do this would be with some clever vectorized NumPy operations. Now the pruning process for a Matchup Ceremony is &lt;a href="https://github.com/daturkel/ayto/blob/a2f4129df2c5d0e13a755a4e0a9b772bd222ffdb/src/ayto/ayto.py#L218"&gt;effectively three lines&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;sums&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scenarios&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;matchup_list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;axis&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sums&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;beams&lt;/span&gt;
&lt;span class="n"&gt;scenarios&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scenarios&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;What&amp;rsquo;s going on here? The variable &lt;code&gt;matchup_list&lt;/code&gt; is a 1-by-11 array of integers representing the pairing we are evaluating (e.g. the pairing chosen at the Matchup Ceremony), and &lt;code&gt;scenarios&lt;/code&gt; is the aforementioned N-by-11 array of possible remaining pairings. Performing the comparison &lt;code&gt;scenarios == matchup_list&lt;/code&gt; &lt;a href="https://numpy.org/doc/stable/user/basics.broadcasting.html"&gt;broadcasts&lt;/a&gt; the smaller array along the first axis of the longer one, meaning we compare the test pairing against every possible pairing in &lt;code&gt;scenarios&lt;/code&gt; and we end up with an &lt;code&gt;N-by-11&lt;/code&gt; array indicating element-wise equality.&lt;/p&gt;
&lt;div class="image"&gt;
&lt;img src="https://danturkel.com/2023/01/25/ayto-step-1.png" alt="A diagram showing how we compare all possible scenarios to a given pairing."&gt;
&lt;/div&gt;

&lt;p&gt;When we take the &lt;code&gt;sum(axis=1)&lt;/code&gt; of that result, we count the number of &lt;code&gt;True&lt;/code&gt; elements in each row and end up with an array of these counts N elements long.&lt;/p&gt;
&lt;div class="image"&gt;
&lt;img src="https://danturkel.com/2023/01/25/ayto-step-2.png" alt="A diagram showing how we count the number of pairings on which scenarios agree with the test scenario."&gt;
&lt;/div&gt;

&lt;p&gt;Lastly, the comparison &lt;code&gt;sums == beams&lt;/code&gt; compares the number of &lt;code&gt;True&lt;/code&gt;s in each row to the number of beams produced at the Matchup Ceremony (broadcasting the comparison again). We end up with a length-N array &lt;code&gt;idx&lt;/code&gt; of booleans, with &lt;code&gt;True&lt;/code&gt; for the indices of scenarios that remain and &lt;code&gt;False&lt;/code&gt; for those that have been eliminated. We can update our list of scenarios with &lt;code&gt;scenarios = scenarios[idx]&lt;/code&gt;.&lt;/p&gt;
&lt;div class="image"&gt;
&lt;img src="https://danturkel.com/2023/01/25/ayto-step-3.png" alt="A diagram showing how we create a boolean index selecting only the scenarios that agree on the correct number of pairings."&gt;
&lt;/div&gt;

&lt;p&gt;Compared to naive looping, this method is about 36 times faster at applying a Matchup Ceremony to the full list of starting scenarios for 11 men and 11 women (0.9 versus 32.1 seconds). A slightly simplified version of the same logic is used for Truth Booths.&lt;/p&gt;
&lt;h4&gt;Calculating probabilities&lt;/h4&gt;
&lt;p&gt;The last step is to take this list of scenarios and calculate all 11×11=121 couple probabilities. We can cheat when we &lt;a href="https://github.com/daturkel/ayto/blob/a2f4129df2c5d0e13a755a4e0a9b772bd222ffdb/src/ayto/ayto.py#L199"&gt;initialize the probabilities&lt;/a&gt; because we know they&amp;rsquo;re all 1/11, but otherwise our strategy is to see how many times each woman is paired with each man and divide it by the total number of remaining scenarios.&lt;/p&gt;
&lt;p&gt;My first attempt at this was (once again) &lt;a href="https://github.com/daturkel/ayto/blob/7d3d8fd64cf37e855d6427e4f477434673b16e71/src/ayto/ayto.py#L139"&gt;plain old Python loops&lt;/a&gt;, building up a counting dictionary. This method takes over 70 seconds to calculate probabilities after the first Truth Booth.&lt;/p&gt;
&lt;p&gt;To speed it up, NumPy saves the day again. We still use a dictionary (a &lt;a href="https://docs.python.org/3/library/collections.html#collections.defaultdict"&gt;defaultdict&lt;/a&gt; actually) to count up how many times each woman is paired with each man, but we use &lt;a href="https://numpy.org/doc/stable/reference/generated/numpy.unique.html"&gt;NumPy&amp;rsquo;s &lt;code&gt;unique&lt;/code&gt; function&lt;/a&gt; to count all the women simultaneously for each man. It looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;probs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defaultdict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;defaultdict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;guy_idx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;guy&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;guys&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# get that guy&amp;#39;s column of the scenarios dataframe&lt;/span&gt;
    &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scenarios&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="n"&gt;guy_idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="c1"&gt;# construct a dictionary counting how many times each girl_idx appears&lt;/span&gt;
    &lt;span class="n"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;counts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;return_counts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;count_dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;girl_idx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;girl&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;girls&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# populate probs dict with probaility = counts / num_scenarios&lt;/span&gt;
        &lt;span class="n"&gt;probs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;guy&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;girl&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;count_dict&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;girl_idx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;num_scenarios&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;At a little over 6 seconds (the slowest operation in the library by far), this is 10 times faster than the naive loop version.&lt;sup id="fnref:5"&gt;&lt;a class="footnote-ref" href="#fn:5"&gt;5&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h4&gt;Other niceties&lt;/h4&gt;
&lt;p&gt;Since I knew that I wanted to publish my work on PyPI, I figured I might as well adopt some best practices when it comes to Python packaging. The project is set up with &lt;code&gt;pyproject.py&lt;/code&gt;, as defined in &lt;a href="https://peps.python.org/pep-0518/"&gt;PEP 518&lt;/a&gt;, rather than the &lt;code&gt;setup.py&lt;/code&gt; of yore. It&amp;rsquo;s got a &lt;a href="https://github.com/daturkel/ayto/tree/main/tests"&gt;test suite&lt;/a&gt; with 100% code coverage and benchmark tests that I used to compare the performance of different implementations as I iterated.&lt;/p&gt;
&lt;p&gt;I also used GitHub Actions for CI: when I create a pull request, it &lt;a href="https://github.com/daturkel/ayto/blob/ce7b53c962949c46a87f36352ecd6200a913a1be/.github/workflows/ci.yml"&gt;automatically&lt;/a&gt; runs my tests and &lt;a href="https://mypy-lang.org/"&gt;mypy&lt;/a&gt; on three versions of Python, and when I create a new tag, it &lt;a href="https://github.com/daturkel/ayto/blob/ce7b53c962949c46a87f36352ecd6200a913a1be/.github/workflows/publish-pypi.yaml"&gt;automatically&lt;/a&gt; publishes it to PyPI.&lt;/p&gt;
&lt;p&gt;And believe it or not, I haven&amp;rsquo;t even mentioned every feature in this blog post. Check out &lt;a href="https://github.com/daturkel/ayto/blob/ce7b53c962949c46a87f36352ecd6200a913a1be/demo.ipynb"&gt;the demo notebook&lt;/a&gt; for a full tour of the library&amp;rsquo;s capabilities.&lt;/p&gt;
&lt;h3&gt;Parting thoughts&lt;/h3&gt;
&lt;p&gt;Whether you&amp;rsquo;re eager to get the latest probabilities each week, or you&amp;rsquo;re just curious to toy around with some code, feel free to &lt;a href="https://github.com/daturkel/ayto/"&gt;check out the ayto repo&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;I hope I&amp;rsquo;ve convinced you that &lt;em&gt;Are You the One?&lt;/em&gt; is more than just another reality TV show. Fans of the coding project may not love the show, and fans of the show may not care about the code, but I find there to be something enjoyable in both.&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Of course there is the third way of learning information which is the contestants getting to know each other and trying to determine—based on genuine human connection—who their matches might be. The show places an, I think, undue amount of emphasis on this component. I prefer to focus on the hard facts over the caprices of the contestants.&amp;#160;&lt;a class="footnote-backref" href="#fnref:1" title="Jump back to footnote 1 in the text"&gt;&amp;uarr;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;The format has varied slightly over the years. Early seasons had 10 men and 10 women, reducing the space of possible pairings by an order of magnitude to 10! (or 3.6 million) pairings. Season 8 had 16 contestants, but all contestants were bisexual so anyone could be paired with anyone else, for a total of &lt;a href="https://math.stackexchange.com/questions/342071/number-of-possible-pairs"&gt;16!/(2&lt;sup&gt;8&lt;/sup&gt;×8!)&lt;/a&gt; or about 2 million pairings.&amp;#160;&lt;a class="footnote-backref" href="#fnref:2" title="Jump back to footnote 2 in the text"&gt;&amp;uarr;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;Or maybe it&amp;rsquo;s fun &lt;em&gt;because&lt;/em&gt; it&amp;rsquo;s tedious. The manual spreadsheet-ing certainly provides a sense of accomplishment, while automating the work can feel a bit too easy at times.&amp;#160;&lt;a class="footnote-backref" href="#fnref:3" title="Jump back to footnote 3 in the text"&gt;&amp;uarr;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;I&amp;rsquo;m not the only one to have this idea. Alex at &lt;a href="http://areuthe.blogspot.com/"&gt;&lt;em&gt;Are You The One?&lt;/em&gt; Math&lt;/a&gt; has been dutifully blogging mathematical commentary since 2014 and, while he hasn&amp;rsquo;t published his code (to my knowledge), I owe him a debt for inspiring me to write my own.&amp;#160;&lt;a class="footnote-backref" href="#fnref:4" title="Jump back to footnote 4 in the text"&gt;&amp;uarr;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:5"&gt;
&lt;p&gt;I also tried two alternatives: 1) Getting the count for each girl by doing &lt;code&gt;count = (col == girl_idx).sum()&lt;/code&gt;, which was marginally slower than using &lt;code&gt;np.unique&lt;/code&gt;. 2) Getting the counts of women for all men simultaneously with &lt;code&gt;np.unique(scenarios, return_counts=True, axis=1)&lt;/code&gt; is insanely slow and might not even do what I want (I never let it run long enough to finish).&amp;#160;&lt;a class="footnote-backref" href="#fnref:5" title="Jump back to footnote 5 in the text"&gt;&amp;uarr;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content><category term="misc"></category><category term="fun"></category><category term="math"></category><category term="programming"></category><category term="python"></category></entry><entry><title>What I learned through Advent of Code 2022</title><link href="https://danturkel.com/2022/12/27/advent-of-code-2022.html" rel="alternate"></link><published>2022-12-27T00:00:00-05:00</published><updated>2022-12-27T00:00:00-05:00</updated><author><name>Dan Turkel</name></author><id>tag:danturkel.com,2022-12-27:/2022/12/27/advent-of-code-2022.html</id><summary type="html">&lt;p&gt;Some Python tricks, algorithms, best practices, and more that I picked up during AoC 2022.&lt;/p&gt;</summary><content type="html">&lt;div class="image"&gt;
&lt;img src="https://danturkel.com/2022/12/27/landscape-with-stars-henri-edmond-cross.jpg" alt="A watercolor painting of a blue night sky with large yellow stars."&gt;
&lt;p&gt;&lt;em&gt;Landscape with Stars&lt;/em&gt; (c. 1905–1908) by Henri-Edmond Cross, via &lt;a href="https://www.metmuseum.org/art/collection/search/459189"&gt;The Met&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;December is a time for bringing out the winter jacket, curling up by the fire with a book, and—for many—participating in &lt;a href="https://adventofcode.com/"&gt;Advent of Code&lt;/a&gt;, an annual set of 25 daily two-part programming puzzles. &lt;/p&gt;
&lt;p&gt;The puzzles are in roughly ascending order of difficulty. In 2020, I gave up after 18 days. Last year I did 15. This year (though I skipped a few along the way) I made it all the way to the end, completing 43 of the 50 total puzzles. &lt;/p&gt;
&lt;p&gt;Some people like to do AoC in a language that&amp;rsquo;s fairly new to them, learning as they go. Others try to do every day in a different language, or to ensure that all solutions run in under a second. The guy who finished in first place on &lt;a href="https://adventofcode.com/2022/leaderboard"&gt;the leaderboard&lt;/a&gt; this year did the puzzles in &lt;a href="https://github.com/betaveros/advent-of-code-2022"&gt;a programming language he invented&lt;/a&gt;&amp;hellip;so there&amp;rsquo;s that too.&lt;/p&gt;
&lt;p&gt;I did the puzzles in Python, the language I know best, but I still managed to learn and try out quite a few new things along the way.&lt;/p&gt;
&lt;p&gt;All my solutions are &lt;a href="https://github.com/daturkel/advent-of-code"&gt;on Github&lt;/a&gt;, for the curious reader&amp;rsquo;s perusal.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; If you&amp;rsquo;re planning on doing the challenges this year and you haven&amp;rsquo;t already, a) what are you waiting for?, and b) this post contains some mild spoilers.&lt;/p&gt;
&lt;h2&gt;The &lt;em&gt;power of friendship&lt;/em&gt;&lt;/h2&gt;
&lt;p&gt;The first 5 or so days of AoC are pretty simple, the next five are reasonable, and after that they start to take a little time. I got a ton of value out of chatting with others about the problems and our approaches, whether that was in my work&amp;rsquo;s #advent_of_code Slack channel, the &lt;a href="https://normconf.com/"&gt;NormConf Slack&lt;/a&gt; daily puzzle threads, or on &lt;a href="https://reddit.com/r/adventofcode"&gt;Reddit&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;I did my best to finish or substantially attempt all the puzzles before resorting to discussion and hints, but there were a few cases where I couldn&amp;rsquo;t have done it without the wisdom of others (or, in &lt;a href="https://github.com/daturkel/advent-of-code/blob/main/2022/18/18.py#L48"&gt;one case&lt;/a&gt;, basically just stealing a few lines after banging my head against the wall for too long). It&amp;rsquo;s really fun to see the sometimes wildly different approaches that people take, whether it&amp;rsquo;s based on their level of experience, their background, or their choice of language.&lt;/p&gt;
&lt;h2&gt;Classic algorithms and data structures&lt;/h2&gt;
&lt;p&gt;Speaking of background knowledge and experience: I only ever took intro to computer science in undergrad, and most of my programming skills are self-taught, so I have a lot of gaps around things you learn college CS courses that you might not pick up in day-to-day programming, namely around algorithms and data structures.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://adventofcode.com/2022/day/12"&gt;Day 12&lt;/a&gt; involved some pathfinding, and I was able to reuse what I wrote for &lt;a href="https://adventofcode.com/2021/day/15"&gt;Day 15 of last year&lt;/a&gt;, which I believe is an &lt;a href="https://github.com/daturkel/advent-of-code/blob/main/2022/12/12.py#L70-L86"&gt;acceptable attempt&lt;/a&gt; at the &lt;a href="https://en.wikipedia.org/wiki/A*_search_algorithm"&gt;A* algorithm&lt;/a&gt; (though I didn&amp;rsquo;t know that at the time) that I cobbled together.&lt;/p&gt;
&lt;p&gt;Several other problems this year were well-suited to &lt;a href="https://en.wikipedia.org/wiki/Breadth-first_search"&gt;breadth-first search&lt;/a&gt;, which is just not something I&amp;rsquo;ve ever formally learned. I managed to make my way through the problems without a real BFS implementation until &lt;a href="https://adventofcode.com/2022/day/24"&gt;day 24&lt;/a&gt; when I was faced with a more complex path-finding problem. I took the &lt;a href="https://en.wikipedia.org/wiki/A*_search_algorithm#Pseudocode"&gt;Wikipedia pseudocode&lt;/a&gt; and turned it into a &lt;a href="https://github.com/daturkel/advent-of-code/blob/main/2022/24/24.py#L65-L93"&gt;reasonable solution&lt;/a&gt; in Python.&lt;/p&gt;
&lt;p&gt;En route, I learned a bit more about queues, a data structure I&amp;rsquo;d never had much use for before. The advantage of a queue over a list comes when we frequently need to a) add to the end and b) take from the front. Adding to the end of a Python list is fast, but removing an item from the front is O(n). Queues, on the other hand, can give you O(1) insertion and removal from the ends.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;d think that the best way to implement a queue in Python would be the &lt;a href="https://docs.python.org/3/library/queue.html"&gt;built-in class&lt;/a&gt; called &lt;code&gt;queue.Queue&lt;/code&gt;, but this is actually quite slow. It turns out that &lt;code&gt;Queue&lt;/code&gt; is better suited to general-purpose communication between threads and incurs overhead to insure thread-safety with &lt;a href="https://en.wikipedia.org/wiki/Lock_(computer_science)"&gt;locks&lt;/a&gt;. On the other hand, &lt;code&gt;collections.deque&lt;/code&gt; implements a double-ended queue without the locks needed for fancy multithreading, and serves our purposes at a much faster clip. In &lt;a href="https://github.com/daturkel/advent-of-code/blob/main/2022/24/24.py"&gt;my solution&lt;/a&gt;, swapping &lt;code&gt;Queue&lt;/code&gt; out for &lt;code&gt;deque&lt;/code&gt; took the solution from 2.5 to 1.5 seconds.&lt;/p&gt;
&lt;h2&gt;Match/case statements&lt;/h2&gt;
&lt;p&gt;New in Python 3.10 is &lt;a href="https://peps.python.org/pep-0634/"&gt;structural pattern-matching&lt;/a&gt;, aka the match/case statement. On the surface, it might seem like a fancy way of rewriting if/else statements, and it basically is. However, the syntax allows for substantially more legible code when dealing with complex sets of conditions.&lt;/p&gt;
&lt;p&gt;I gave pattern-matching a shot and ended up using it in three puzzles, to great success.&lt;/p&gt;
&lt;p&gt;On &lt;a href="https://adventofcode.com/2022/day/7"&gt;Day 7&lt;/a&gt;, you have to parse a set of faux command line commands and output that look something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ cd a
$ ls
dir e
29116 f
2557 g
62596 h.lst
$ cd e
$ ls
584 i
$ cd ..
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Using pattern-matching made writing &lt;a href="https://github.com/daturkel/advent-of-code/blob/main/2022/07/07.py#L29-L44"&gt;my parsing code&lt;/a&gt; much neater than writing a bunch of awkward &lt;code&gt;if&lt;/code&gt;/&lt;code&gt;elif&lt;/code&gt; statements (gratuitously commented here for exposition):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# for each line, we split the line into separate words&lt;/span&gt;
&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;

    &lt;span class="c1"&gt;# if we&amp;#39;ve got 3 words where the second is &amp;quot;cd&amp;quot; and the third is &amp;quot;..&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;cd&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;..&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="c1"&gt;# deal with the &amp;quot;go up a directory&amp;quot; case&lt;/span&gt;
        &lt;span class="n"&gt;folder_sizes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_size&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt; &lt;span class="c1"&gt;# type: ignore&lt;/span&gt;

    &lt;span class="c1"&gt;# if we&amp;#39;ve got 3 words where the second is &amp;quot;cd&amp;quot; and the third is something else&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;cd&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dir_name&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="c1"&gt;# deal with the &amp;quot;go down a directory&amp;quot; case&lt;/span&gt;
        &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;children&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;dir_name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dir_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;children&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;dir_name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# if we&amp;#39;ve got 2 words and the second is &amp;quot;ls&amp;quot; *or* the first is &amp;quot;dir&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;ls&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;dir&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="c1"&gt;# deal with those cases (which actually don&amp;#39;t require anything)&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="c1"&gt;# if we&amp;#39;ve got any other case of 2 words, it&amp;#39;s a filesize and a file&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;children&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A line like &lt;code&gt;case [_, "cd", dir_name]&lt;/code&gt; simultaneously checks that &lt;code&gt;line.split()&lt;/code&gt; is three values long, and that the second one is the string &lt;code&gt;"cd"&lt;/code&gt;, and then lets us use the variable &lt;code&gt;dir_name&lt;/code&gt; to capture the third value for the contents of the clause. This is great syntactic sugar for a fancy if statement and, in my opinion, very readable.&lt;/p&gt;
&lt;p&gt;On &lt;a href="https://adventofcode.com/2022/day/11"&gt;Day 11&lt;/a&gt;, we&amp;rsquo;re once again parsing a predictable series of inputs with different semantics, and this time &lt;a href="https://github.com/daturkel/advent-of-code/blob/main/2022/11/11b.py#L34-L54"&gt;I used&lt;/a&gt; the &lt;a href="https://peps.python.org/pep-0635/#wildcard-pattern"&gt;wildcard pattern&lt;/a&gt; to capture an indeterminate number of values:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Starting&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;items:&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;items_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;[&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;]&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And on &lt;a href="https://adventofcode.com/2022/day/13"&gt;Day 13&lt;/a&gt;, I &lt;a href="https://github.com/daturkel/advent-of-code/blob/main/2022/13/13.py#L25-L37"&gt;had the chance&lt;/a&gt; to use &lt;a href="https://peps.python.org/pep-0636/#matching-builtin-classes"&gt;type-based pattern-matching&lt;/a&gt; to distinguish between cases based on the types of the values:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;()]:&lt;/span&gt;
        &lt;span class="c1"&gt;# do int comparisons&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="n"&gt;this_comparison&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;this_comparison&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;()]:&lt;/span&gt;
        &lt;span class="n"&gt;this_comparison&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;compare&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;()]:&lt;/span&gt;
        &lt;span class="n"&gt;this_comparison&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;compare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;()]:&lt;/span&gt;
        &lt;span class="n"&gt;this_comparison&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;compare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;All in all, I was impressed with the flexibility and power of the syntax and I&amp;rsquo;m looking forward to using this in the future for making complex conditionals more human-readable.&lt;/p&gt;
&lt;div class="image"&gt;
&lt;img src="https://danturkel.com/2022/12/27/swifts-giacomo-balla.jpg" alt="Ab abstract, futurist painting with busy, repeating geometric patterns."&gt;
&lt;p&gt;&lt;em&gt;Swifts: Paths of Movement + Dynamic Sequences&lt;/em&gt; (1913) by Giacomo Balla, via &lt;a href="https://www.moma.org/collection/works/79347?artist_id=311"&gt;MoMA&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;

&lt;h2&gt;Python&amp;rsquo;s built-in profilers&lt;/h2&gt;
&lt;p&gt;Python is somewhat infamous for prioritizing ergonomics and dynamism over raw performance (though it&amp;rsquo;s getting &lt;a href="https://docs.python.org/3/whatsnew/3.11.html#faster-cpython"&gt;faster&lt;/a&gt;), but that doesn&amp;rsquo;t mean it has to be &lt;em&gt;slow&lt;/em&gt;. There were a number of problems this year where your approach, choice of data structures, and implementation decisions could make the difference between the code completing in less than a second or maybe not at all.&lt;/p&gt;
&lt;p&gt;I got a ton of value out of Python&amp;rsquo;s &lt;a href="https://docs.python.org/3/library/profile.html"&gt;cProfile&lt;/a&gt;. For my purposes, a simple &lt;code&gt;python -m cProfile my_script.py&lt;/code&gt; was all it took to get a bunch of output on which functions or operations were taking the longest. Even if you kill the script before it finishes (because maybe it won&amp;rsquo;t finish&amp;hellip;), you still get the profiler output to figure out what your program is stuck on. It was a great way to find the bottlenecks in my solutions, like an inefficient loop, a list where I could&amp;rsquo;ve used a set, or a class where I could&amp;rsquo;ve just used a tuple. In fact, it was the profiler that showed me that the aforementioned &lt;code&gt;Queue&lt;/code&gt; was spending lots of time on functions imported from &lt;code&gt;threading.py&lt;/code&gt; and revealed that it wasn&amp;rsquo;t the right data structure I needed.&lt;/p&gt;
&lt;h2&gt;A quick-and-dirty cache&lt;/h2&gt;
&lt;p&gt;A common source of grief for beginning Python developers is &lt;a href="https://docs.python-guide.org/writing/gotchas/"&gt;mutable default arguments&lt;/a&gt;. It can be &lt;a href="https://stackoverflow.com/questions/1132941/least-astonishment-and-the-mutable-default-argument"&gt;surprising&lt;/a&gt; to find that your function has become stateful and the results are changing with each call.&lt;/p&gt;
&lt;p&gt;However, this same behavior also lets us easily add a cache to a function. For instance, on &lt;a href="https://adventofcode.com/2022/day/24"&gt;Day 24&lt;/a&gt;, you have to deal with a 2D grid and you&amp;rsquo;ll frequently want to generate a list of the points which neighbor a given point without going out of bounds. This is a fairly simple operation, but caching it gives us a nice performance boost:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Grid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
        &lt;span class="c1"&gt;# omitted for brevity&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;neighbors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;point&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;point&lt;/span&gt;
        &lt;span class="n"&gt;candidates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;candidates&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That &lt;code&gt;cache: dict = {}&lt;/code&gt; in the function signature is where the magic happens. An empty dictionary is created when the &lt;code&gt;neighbors&lt;/code&gt; function is defined, and that same dictionary (along with anything that gets added to it inside the function) is reused on every subsequent call, allowing us to skip the function body for a dictionary lookup if we&amp;rsquo;ve already computed the result we need. &lt;/p&gt;
&lt;p&gt;With the cache, the script solves the puzzle in about 1.5 seconds (admittedly, one of my slower solutions for all of AoC). Without the cache, it finishes in 3.1 seconds. Removing the cache from &lt;a href="https://github.com/daturkel/advent-of-code/blob/main/2022/24/24.py#L29"&gt;another more expensive function in the same solution&lt;/a&gt; brings the runtime to 382.0 seconds. Caching matters, and this is a very low-lift way to add one.&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h2&gt;Type hinting is getting better all the time&lt;/h2&gt;
&lt;p&gt;Since version 3.5, Python has supported optional &lt;a href="https://peps.python.org/pep-0484/"&gt;type hints&lt;/a&gt;: annotations in source code that indicate the &lt;a href="https://en.wikipedia.org/wiki/Data_type"&gt;type&lt;/a&gt; of a variable. This is somewhat tricky business in a language as dynamic as Python, but when combined with type-checking tools like &lt;a href="http://mypy-lang.org/"&gt;mypy&lt;/a&gt; and clever IDE integrations, it can surface bugs early and also serve as great documentation for your future readers.&lt;/p&gt;
&lt;p&gt;But typing was a late addition to Python, and the original implementation was a bit less-than-ergonomic, requiring frequent imports from the &lt;code&gt;typing&lt;/code&gt; library for anything beyond the most basic annotation. Language enhancements in recent Python versions have made the process much more seamless, and I took advantage of some of those features to add annotations to all my solutions and ensure they all passed mypy checks.&lt;/p&gt;
&lt;p&gt;Python 3.9 added &lt;a href="https://docs.python.org/3/whatsnew/3.9.html#type-hinting-generics-in-standard-collections"&gt;&amp;ldquo;type hinting generics in standard collections,&amp;rdquo;&lt;/a&gt; which is a fancy way of saying that if you want to annotate a list of ints, you can just write &lt;code&gt;numbers: list[int]&lt;/code&gt; rather than needing to import &lt;code&gt;List&lt;/code&gt; from the &lt;code&gt;typing&lt;/code&gt; module to annotate &lt;code&gt;List[int]&lt;/code&gt;. It&amp;rsquo;s a small thing, but it goes a long way in making typing feel more like a first-class feature of the language rather than this odd thing that you can do with an extra module.&lt;/p&gt;
&lt;p&gt;Along the same lines, Python 3.10 added the &lt;a href="https://docs.python.org/3/whatsnew/3.10.html#pep-604-new-type-union-operator"&gt;union operator&lt;/a&gt;, so a variable that could be a float or a list of floats can be annotated &lt;code&gt;foo: float | list[float]&lt;/code&gt; rather than &lt;code&gt;number:  Union[float, list[float]]&lt;/code&gt; (after importing &lt;code&gt;Union&lt;/code&gt; from &lt;code&gt;typing&lt;/code&gt;). This also lets you avoid importing &lt;code&gt;Optional&lt;/code&gt;: instead you just write &lt;code&gt;foo: float | None&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I also heavily used &lt;a href="https://peps.python.org/pep-0484/#type-aliases"&gt;type aliases&lt;/a&gt;, which have apparently been around for a while but which I seem to have missed. It was pretty common to be dealing with tuples of two ints representing coordinates on a grid, and maybe a function returns a list of these coordinates. Without type aliases, we&amp;rsquo;d annotate the return type as &lt;code&gt;list[tuple[int, int]]&lt;/code&gt;. However, with a simple &lt;code&gt;Point = tuple[int, int]&lt;/code&gt;, we can annotate this as &lt;code&gt;list[Point]&lt;/code&gt; instead. Much better!&lt;/p&gt;
&lt;h2&gt;&amp;hellip;but it still has some rough edges&lt;/h2&gt;
&lt;p&gt;The above notwithstanding, all is still not perfect in Python type world. For one thing, &lt;a href="https://peps.python.org/pep-0484/#forward-references"&gt;forward references&lt;/a&gt; (annotations referring to types not yet defined) still have to be annotated as strings, as was the case in a graph node class I implemented for &lt;a href="https://github.com/daturkel/advent-of-code/blob/main/2022/07/07.py#L12"&gt;day 7&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Node | None&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;"Node | None"&lt;/code&gt; annotation is the forward reference and it feels hacky. Arguably it should at least be &lt;code&gt;"Node" | None&lt;/code&gt;, which seems to be &lt;a href="https://bugs.python.org/issue45857"&gt;under discussion&lt;/a&gt; but isn&amp;rsquo;t yet supported. &lt;strong&gt;Edit:&lt;/strong&gt; &lt;a href="https://joelgrus.com/"&gt;Joel Grus&lt;/a&gt; pointed out that this can be fixed with &lt;code&gt;from __future__ import annotations&lt;/code&gt;, after which the &lt;code&gt;Node | None&lt;/code&gt; annotation works fine. This fixed behavior will be added to a future version of Python. Thanks, Joel!&lt;/p&gt;
&lt;p&gt;Worse yet, at least twice I had to cast mypy asside witih a &lt;code&gt;# type: ignore&lt;/code&gt; comment to dismiss some nagging edge case I couldn&amp;rsquo;t fix. In that same puzzle, there is a case where I&amp;rsquo;m dealing with that same troublesome &lt;code&gt;Node.parent&lt;/code&gt; attribute, which is either a &lt;code&gt;Node&lt;/code&gt; or &lt;code&gt;None&lt;/code&gt;. In &lt;a href="https://github.com/daturkel/advent-of-code/blob/main/2022/07/07.py#L34"&gt;the context&lt;/a&gt;, I know it&amp;rsquo;s a real &lt;code&gt;Node&lt;/code&gt; and I happily assign its value to a variable which is also a &lt;code&gt;Node&lt;/code&gt;, but the type-checker still thinks it could be &lt;code&gt;None&lt;/code&gt; (it has no way of knowing it&amp;rsquo;s not) and throws an error. I believe the canonical solution is to add an &lt;code&gt;if foo is not None:&lt;/code&gt; clause in there so that mypy is sure the attribute is non-null in this case, but it feels wrong to add logic to the code just to satisfy the type-checker.&lt;/p&gt;
&lt;p&gt;I also found that the value of adding type hints to my puzzle solutions was fairly low. In production code, I&amp;rsquo;ve had mypy detect sneaky bugs where I was unknowingly relying on behind-the-scenes type coercion rather than being careful about what I pass around. But in Advent of Code the annotations mostly end up being overkill—at best they helped me with rapid refactorings as I rewrote a solution-in-progress.&lt;/p&gt;
&lt;div class="image"&gt;
&lt;img src="https://danturkel.com/2022/12/27/walrus-hunt-elizabeth-fitzgerald.jpg" alt="Pen and ink illustration of hunters in a boat with guns drawn at a pack of walruses."&gt;
&lt;p&gt;&lt;em&gt;Walrus Hunt&lt;/em&gt; (1801) by Elizabeth Fitzgerald, via &lt;a href="https://www.artic.edu/artworks/95819/walrus-hunt"&gt;the Art Institute of Chicago&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;

&lt;h2&gt;Classes as a last resort&lt;/h2&gt;
&lt;p&gt;It&amp;rsquo;s easy to make a habit of reaching for a class whenever you start dealing with instances of structured data. I resisted the instinct to class-ify everything during AoC. In one instance, I refactored a solution to remove a class, &lt;a href="https://github.com/daturkel/advent-of-code/blob/main/2022/24/24.py#L66"&gt;replacing it with a tuple&lt;/a&gt;: the profiler showed me that the overhead of millions of &lt;code&gt;__init__&lt;/code&gt; calls were slowing my solution down and I wasn&amp;rsquo;t even getting much value out of the class.&lt;sup id="fnref:2"&gt;&lt;a class="footnote-ref" href="#fn:2"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;I ended up writing custom classes for 7 of the 25 days, 5 of which were &lt;code&gt;Grid&lt;/code&gt; or &lt;code&gt;Board&lt;/code&gt; classes that I used to manage movement, storage, and display of points around 2D/3D spaces. The presence of structured data alone is not enough to warrant a class, but when you start to accumulate functions to act on that data and have state that you need to keep track of, a class comes in handy to keep things tidy.&lt;/p&gt;
&lt;p&gt;Some people are big fans of Python&amp;rsquo;s &lt;a href="https://docs.python.org/3/library/dataclasses.html"&gt;dataclasses&lt;/a&gt;, which allow you to easily define classes for structured data with less boilerplate, but I&amp;rsquo;m not sure I count myself among them just yet. I originally had a dataclass in one or two of my solutions, but I must have refactored them out before finishing. I found that if I only needed a dataclass, I likely didn&amp;rsquo;t need a full class at all and could replace it with a tuple. In the instances where I &lt;em&gt;did&lt;/em&gt; need a class, I had enough initialization logic that I needed to write my own &lt;code&gt;__init__&lt;/code&gt; function, and the dataclass didn&amp;rsquo;t offer me much.&lt;/p&gt;
&lt;h2&gt;The walrus operator: Still not for me&lt;/h2&gt;
&lt;p&gt;Python 3.8 introduced &lt;a href="https://docs.python.org/3/whatsnew/3.8.html#assignment-expressions"&gt;assignment expressions&lt;/a&gt; or, as they&amp;rsquo;re more commonly known, the &amp;ldquo;walrus operator&amp;rdquo;. It looks like &lt;code&gt;:=&lt;/code&gt; and if you squint it kinda looks like a sideways walrus. It&amp;rsquo;s &lt;a href="https://peps.python.org/pep-0572/"&gt;proposal&lt;/a&gt; was the source of near-infinite controversy and drama and the whole affair contributed to Python&amp;rsquo;s creator Guido van Rossum &lt;a href="https://www.infoworld.com/article/3292936/guido-van-rossum-resigns-whats-next-for-python.html"&gt;stepping down&lt;/a&gt; from his role as the language&amp;rsquo;s Benevolent Dictator for Life.&lt;/p&gt;
&lt;p&gt;The walrus operator lets you assign a value mid-expression. It&amp;rsquo;s best demonstrated with an example (adapted from the docs):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Without walrus operator&lt;/span&gt;
&lt;span class="c1"&gt;# a) separate line for assigning a variable that we will reuse&lt;/span&gt;
&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;List is too long (&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; elements, expected &amp;lt;= 10)&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# b) calculate the length twice&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;List is too long (&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; elements, expected &amp;lt;= 10)&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# With the walrus operator&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;List is too long (&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; elements, expected &amp;lt;= 10)&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Option (b) is potentially bad for performance if you&amp;rsquo;re doing something more intense than calculating a length. Option (a), however, suits me perfectly fine and all the walrus operator really does is save us a line.&lt;/p&gt;
&lt;p&gt;I wanted to keep an open mind and give it a shot if a reasonable use case came up, but I never saw the occasion to use it and even while going back through my solutions I couldn&amp;rsquo;t find a place where it would make sense to swap it in. I have no strong feelings one way or another about the feature, but my code remains walrus-free, maybe because of my last point&amp;hellip;&lt;/p&gt;
&lt;h2&gt;Verbose is better than terse&lt;/h2&gt;
&lt;p&gt;Okay, so this isn&amp;rsquo;t something I learned this time around, but its importance was apparent again and again, especially as I was writing this post and rereading solutions from weeks ago but also as I was debugging almost-working solutions. Your colleagues and &lt;em&gt;future you&lt;/em&gt; will appreciate and better understand code that is written clearly, not code that is &lt;a href="https://en.wikipedia.org/wiki/Code_golf"&gt;golfed&lt;/a&gt; to a minimum number of lines or characters.&lt;/p&gt;
&lt;p&gt;To be fair, for some people a big part of the fun is writing their solutions in as few lines as possible, and that&amp;rsquo;s okay too! I often checked out &lt;a href="https://www.reddit.com/r/adventofcode/"&gt;r/adventofcode&lt;/a&gt; after completing a puzzle and was shocked to find that someone else had written a Python solution in a dozen lines that I&amp;rsquo;d finished in 100, and I invariably learned something new from those solutions—if I could make sense of them. But as someone who was using AoC to practice and enhance my better programming practices, I found it valuable to take the long road. More verbose code makes code comprehension and debugging easier.&lt;/p&gt;
&lt;p&gt;This is arguably captured by few of the lines of the &lt;a href="https://peps.python.org/pep-0020/"&gt;Zen of Python&lt;/a&gt;: &amp;ldquo;Beautiful is better than ugly,&amp;rdquo; &amp;ldquo;Explicit is better than implicit,&amp;rdquo; &amp;ldquo;Simple is better than complex,&amp;rdquo; and especially &amp;ldquo;Readability counts.&amp;rdquo; &lt;/p&gt;
&lt;p&gt;I hope that I stayed true enough to this principle that others can learn something from &lt;a href="https://github.com/daturkel/advent-of-code"&gt;my solutions&lt;/a&gt; if they&amp;rsquo;d like.&lt;/p&gt;
&lt;p&gt;Of course, there&amp;rsquo;s a limit to just how verbose one needs to be, and so I think I&amp;rsquo;ll end this post here. See you all again next Advent of Code.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Special thanks to &lt;a href="https://twitter.com/timhochberg"&gt;Tim Hochberg&lt;/a&gt; and &lt;a href="https://www.peterbaumgartner.com/"&gt;Peter Baumgartner&lt;/a&gt; who gave this post an early read.&lt;/em&gt;&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Early reader &lt;a href="https://twitter.com/timhochberg"&gt;Tim Hochberg&lt;/a&gt; pointed out that Python also provides a &lt;a href="https://docs.python.org/3/library/functools.html#functools.cache"&gt;similar caching solution&lt;/a&gt; in the form of &lt;code&gt;functools.cache&lt;/code&gt;, a decorator which implements the same caching logic for you. This is a great catch and I had simply forgotten about it. The default argument method offers slightly more flexibility, for example: when you need to manipulate your function arguments before using them as cache keys (maybe the function takes a list, but you need to convert it to a tuple before it can be a dict key) or if you want to be able to pre-populate the cache. I used a prepopulated cache (though not via a default argument) in &lt;a href="https://github.com/daturkel/advent-of-code/blob/main/2022/21/21b.py#L13-L15"&gt;one solution&lt;/a&gt;.&amp;#160;&lt;a class="footnote-backref" href="#fnref:1" title="Jump back to footnote 1 in the text"&gt;&amp;uarr;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;Actually, it was worse than that. In this problem, we have to find the shortest path through a storm. I had initially been keeping track of paths as linked lists of nodes where each node knew its coordinates and pointed to its parent. The length of the path could then be calculated as 1 if there was no parent, or else 1 plus the length of the parent. This felt clever at the time, but led to tons of recursion just to get the path length. My next step was to just manually add 1 to a static length attribute each time I created a node, and it was at that point that I realized that I could drop the class and just switch to a tuple of coordinates and length.&amp;#160;&lt;a class="footnote-backref" href="#fnref:2" title="Jump back to footnote 2 in the text"&gt;&amp;uarr;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content><category term="misc"></category><category term="programming"></category><category term="python"></category></entry><entry><title>Late 2016, early 2017</title><link href="https://danturkel.com/2022/06/13/late-2016-early-2017.html" rel="alternate"></link><published>2022-06-13T00:00:00-04:00</published><updated>2022-06-13T00:00:00-04:00</updated><author><name>Dan Turkel</name></author><id>tag:danturkel.com,2022-06-13:/2022/06/13/late-2016-early-2017.html</id><summary type="html">&lt;p&gt;Taken in and around Crown Heights, New York, roughly around the end of 2016 and start of 2017. Kodak Ultramax 400 film in a Nikon N65. Gently post-processed.&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;em&gt;Taken in and around Crown Heights, New York, roughly around the end of 2016 and start of 2017. Kodak Ultramax 400 film in a Nikon N65. Gently post-processed.&lt;/em&gt;&lt;/p&gt;
&lt;div class="image large"&gt;
&lt;a href="https://danturkel.com/2022/06/13/62350004.jpg" &gt;
&lt;img src="https://danturkel.com/2022/06/13/62350004.jpg"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2022/06/13/62350002.jpg" &gt;
&lt;img src="https://danturkel.com/2022/06/13/62350002.jpg"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2022/06/13/62350005.jpg" &gt;
&lt;img src="https://danturkel.com/2022/06/13/62350005.jpg"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image large"&gt;
&lt;a href="https://danturkel.com/2022/06/13/62350007.jpg" &gt;
&lt;img src="https://danturkel.com/2022/06/13/62350007.jpg"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2022/06/13/62350008.jpg" &gt;
&lt;img src="https://danturkel.com/2022/06/13/62350008.jpg"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2022/06/13/62350010.jpg" &gt;
&lt;img src="https://danturkel.com/2022/06/13/62350010.jpg"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2022/06/13/62350014.jpg" &gt;
&lt;img src="https://danturkel.com/2022/06/13/62350014.jpg"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image large"&gt;
&lt;a href="https://danturkel.com/2022/06/13/62350016.jpg" &gt;
&lt;img src="https://danturkel.com/2022/06/13/62350016.jpg"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2022/06/13/62350019.jpg" &gt;
&lt;img src="https://danturkel.com/2022/06/13/62350019.jpg"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2022/06/13/62350021.jpg" &gt;
&lt;img src="https://danturkel.com/2022/06/13/62350021.jpg"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image large"&gt;
&lt;a href="https://danturkel.com/2022/06/13/62350027.jpg" &gt;
&lt;img src="https://danturkel.com/2022/06/13/62350027.jpg"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2022/06/13/62350022.jpg" &gt;
&lt;img src="https://danturkel.com/2022/06/13/62350022.jpg"&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;div class="image"&gt;
&lt;a href="https://danturkel.com/2022/06/13/62350034.jpg" &gt;
&lt;img src="https://danturkel.com/2022/06/13/62350034.jpg"&gt;
&lt;/a&gt;&lt;/div&gt;</content><category term="misc"></category><category term="photos"></category></entry><entry><title>Goals for 2022</title><link href="https://danturkel.com/2021/12/31/goals-for-2022.html" rel="alternate"></link><published>2021-12-31T00:00:00-05:00</published><updated>2021-12-31T00:00:00-05:00</updated><author><name>Dan Turkel</name></author><id>tag:danturkel.com,2021-12-31:/2021/12/31/goals-for-2022.html</id><summary type="html">&lt;p&gt;I&amp;rsquo;m typically pretty averse to making &amp;ldquo;new year&amp;rsquo;s resolutions,&amp;rdquo; but in the spirit of learning in public and holding myself accountable, I figured I&amp;rsquo;d put a few goals out there that I can check in against throughout the year.&lt;/p&gt;</summary><content type="html">&lt;div class="image"&gt;
&lt;img src="https://danturkel.com/2021/12/31/a-new-year-on-the-cimarron-remington.jpg" alt="A painting by Frederic Remington of two cowboys and their horses at a small fire."&gt;
&lt;p&gt;&lt;em&gt;A New Year on the Cimarron&lt;/em&gt; (1903) by Frederic Remington, &lt;a href="https://commons.wikimedia.org/wiki/File:Frederic_Remington_-_A_New_Year_on_the_Cimarron_-_Google_Art_Project.jpg"&gt;via Wikimedia Commons&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;I&amp;rsquo;m typically pretty averse to making &amp;ldquo;new year&amp;rsquo;s resolutions,&amp;rdquo; but in the spirit of &lt;a href="https://www.swyx.io/learn-in-public/"&gt;learning in public&lt;/a&gt; and holding myself accountable, I figured I&amp;rsquo;d put a few goals out there that I can check in against throughout the year.&lt;/p&gt;
&lt;h2&gt;Write more&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;d like to &lt;strong&gt;write at least 12 posts&lt;/strong&gt; on this site. There are no strict rules on what constitutes a &amp;ldquo;post,&amp;rdquo; so it can be as simple as a monthly update or as in-depth as &lt;a href="https://danturkel.com/2021/12/06/belated-highlights-from-recsys-2021.html"&gt;my last post&lt;/a&gt;. I&amp;rsquo;ve got a few topics of varying degrees of complexity in my backlog and it stands to reason that inspiration should strike at least a few more times between now and next January.&lt;/p&gt;
&lt;p&gt;You can keep up on what I end up writing via &lt;a href="https://danturkel.com/feeds/rss.xml"&gt;RSS&lt;/a&gt;. If you&amp;rsquo;d prefer to be notified via email, you can paste the RSS feed URL into a service like &lt;a href="https://feedrabbit.com/"&gt;Feedrabbit&lt;/a&gt; or &lt;a href="https://blogtrottr.com/"&gt;Blogtrottr&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Read more&lt;/h2&gt;
&lt;p&gt;According to &lt;a href="https://www.goodreads.com/user_challenges/26921687"&gt;Goodreads&lt;/a&gt;, I finished 20 books in 2021. My goal for last year was 30, which was probably a bit ambitious. Splitting the difference, I&amp;rsquo;ll set a goal to &lt;strong&gt;read 25 books&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Furthermore, I&amp;rsquo;d like at least 3 of those books to be nominally &amp;ldquo;technical,&amp;rdquo; i.e. vaguely related to my work (statistics, machine learning, software engineering, and so on). I&amp;rsquo;m already a good chunk of the way into &lt;em&gt;&lt;a href="https://www.manning.com/books/the-programmers-brain"&gt;The Programmer&amp;rsquo;s Brain&lt;/a&gt;&lt;/em&gt; by Felienne Hermans, so that&amp;rsquo;s a nice head start. I also make progress in fits and starts through &lt;em&gt;&lt;a href="https://dataintensive.net/"&gt;Designing Data-Intensive Applications&lt;/a&gt;&lt;/em&gt;, but it&amp;rsquo;s a heady 600+ pager, so we&amp;rsquo;ll see how that goes. If you&amp;rsquo;ve got a suggestion for a good read on anything loosely related to production data science, &lt;a href="mailto:daturkel@gmail.com"&gt;I&amp;rsquo;d love to hear it&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Learn more&lt;/h2&gt;
&lt;p&gt;I have access to Udemy and Ardan Labs through work, so this past year I took &lt;a href="https://www.udemy.com/course/go-the-complete-developers-guide/"&gt;a Go course&lt;/a&gt; and &lt;a href="https://www.udemy.com/course/protocol-buffers/"&gt;a short course on Protobuf&lt;/a&gt;, as well as the &lt;a href="https://courses.ardanlabs.com/bundles/intensive-docker-kubernetes-bundle"&gt;Ardan Labs&amp;rsquo; Docker course&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Next year I&amp;rsquo;d like to &lt;strong&gt;take (at least) one online course&lt;/strong&gt;—and that&amp;rsquo;s not counting the &lt;a href="https://courses.ardanlabs.com/bundles/intensive-docker-kubernetes-bundle"&gt;Ardan Labs Kubernetes course&lt;/a&gt; that I&amp;rsquo;ll also be taking through work. Maybe this is the year I&amp;rsquo;ll finally finish &lt;a href="https://github.com/CamDavidsonPilon/Probabilistic-Programming-and-Bayesian-Methods-for-Hackers"&gt;Bayesian Methods for Hackers&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;d also like to &lt;strong&gt;start having conversations in French&lt;/strong&gt;. I&amp;rsquo;ve been learning very basic French on &lt;a href="https://babbel.com/"&gt;Babbel&lt;/a&gt;, but it&amp;rsquo;s about time I take the next step and start taking classes with a human teacher (in some pandemic-friendly format). I want to be able to spontaneously produce sentences rather than just being able to slowly make sense of written text.&lt;/p&gt;
&lt;h2&gt;Take more photos&lt;/h2&gt;
&lt;div class="image"&gt;
&lt;img src="https://danturkel.com/2021/12/31/budapest-0350-dan-turkel.jpg" alt="A photograph of a statue of a soldier against a backdrop of trees."&gt;
&lt;p&gt;A photo I took on a trip to Budapest in the summer of 2018—just one of thousands I've yet to properly go through.&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;I love to take photographs, and I take photos on my phone &lt;a href="https://www.instagram.com/daturkel/"&gt;all the time&lt;/a&gt;. However, I also have a Nikon D7200 that I&amp;rsquo;m not taking advantage of nearly enough, so I&amp;rsquo;d like to &lt;strong&gt;shoot two new batches of photos and post the highlights to this site&lt;/strong&gt; this year.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve also got thousands of years-old photos from trips to Hawaii, Budapest, Prague, and Berlin that I haven&amp;rsquo;t yet processed, so it&amp;rsquo;d be nice to make some progress sorting through that backlog too.&lt;/p&gt;
&lt;h2&gt;See you in 2022&lt;/h2&gt;
&lt;p&gt;That seems like plenty to take on already, so I&amp;rsquo;ll stop here. If you want to keep up with me in 2022, you can find me on &lt;a href="https://twitter.com/daturkel"&gt;Twitter&lt;/a&gt; or follow my writing &lt;a href="https://danturkel.com/feeds/rss.xml"&gt;via RSS&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;Happy new year! 🎊 🎉&lt;/p&gt;</content><category term="misc"></category><category term="personal"></category></entry><entry><title>Belated highlights from RecSys 2021</title><link href="https://danturkel.com/2021/12/06/belated-highlights-from-recsys-2021.html" rel="alternate"></link><published>2021-12-06T00:00:00-05:00</published><updated>2021-12-06T00:00:00-05:00</updated><author><name>Dan Turkel</name></author><id>tag:danturkel.com,2021-12-06:/2021/12/06/belated-highlights-from-recsys-2021.html</id><summary type="html">&lt;p&gt;RecSys focuses on all things related to recommender systems: models which (roughly speaking) predict what items a user will like. Not only are recommenders what I&amp;rsquo;ve been working on for the last couple of years, but it&amp;rsquo;s also a topic I find personally fascinating, and I hope you will too.&lt;/p&gt;</summary><content type="html">&lt;div class="image"&gt;
&lt;img src="https://danturkel.com/2021/12/06/scene-of-amsterdam-kuwasseg.jpg" alt="A 19th-century oil painting of Amsterdam by Charles Euphrasie Kuwasseg."&gt;
&lt;p&gt;From &lt;em&gt;Scene of Amsterdam&lt;/em&gt; (19th century) by Charles Euphrasie Kuwasseg, &lt;a href="https://www.dorotheum.com/en/l/6036774/"&gt;via Dorotheum&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;In September, I virtually attended ACM&amp;rsquo;s RecSys conference, which was held in Amsterdam this year. I watched nearly 70 talks, ranging from keynotes to quick poster sessions, and below I&amp;rsquo;ve collected thoughts on some of the talks that I most enjoyed. I&amp;rsquo;ll also come back and add links to the talk videos once they&amp;rsquo;ve been posted.&lt;/p&gt;
&lt;p&gt;For those unfamiliar, RecSys focuses on all things related to &lt;a href="https://en.wikipedia.org/wiki/Recommender_system"&gt;recommender systems&lt;/a&gt;: models which (roughly speaking) predict what items a user will like. Not only are recommenders what I&amp;rsquo;ve been working on for the last couple of years, but it&amp;rsquo;s also a topic I find personally fascinating, and I hope you will too.&lt;/p&gt;
&lt;p&gt;P.S. Some other folks also published their highlights of the conference. Check out Eugene Yan&amp;rsquo;s &lt;a href="https://eugeneyan.com/writing/recsys2021/"&gt;&amp;ldquo;Papers and Talks to Chew on&amp;rdquo;&lt;/a&gt;, Vicki Boykis&amp;rsquo; &lt;a href="http://veekaybee.github.io/2021/10/28/recsys-recap/"&gt;RecSys 2021 Recap&lt;/a&gt;, Balázs Hidasi&amp;rsquo;s &lt;a href="https://www.yusp.com/blog-posts/recsys-2021-impressions-summary/"&gt;Impressions and Summary&lt;/a&gt; for more. &lt;/p&gt;
&lt;h2&gt;Practical issues&lt;/h2&gt;
&lt;p&gt;These talks focus on issues surrounding recommender systems in production and, as a result, come largely from industry.&lt;/p&gt;
&lt;div class="image"&gt;
&lt;img src="https://danturkel.com/2021/12/06/twee-jongens-en-en-ezel-de-geluk.jpg" alt="A 20th-century print of two boys and a donkey by Julie de Geluk."&gt;
&lt;p&gt;&lt;em&gt;Twee jongens en een ezel&lt;/em&gt; (20th century) by Julie de Geluk, &lt;a href="https://www.rijksmuseum.nl/nl/collectie/RP-P-1935-976"&gt;via the Rijksmuseum&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;

&lt;h3&gt;Jointly optimize capacity, latency, and engagement in large-scale recommendation systems&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Presented by Hitesh Khandelwal from Facebook. Paper at &lt;a href="https://dl.acm.org/doi/abs/10.1145/3460231.3474606"&gt;ACM&lt;/a&gt; and &lt;a href="https://research.fb.com/publications/jointly-optimize-capacity-latency-and-engagement-in-large-scale-recommendation-systems/"&gt;Facebook Research&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Facebook has &lt;a href="https://www.wsj.com/articles/the-facebook-files-11631713039"&gt;been in the news a lot lately&lt;/a&gt; for failing to prevent harms caused by their platform, even when their research shows that those harms are real. A user&amp;rsquo;s experience with Facebook (and Instagram) is heavily mediated through their interaction with a number of different recommender systems, which makes Facebook&amp;rsquo;s present situation an important case study for anyone interested in ethical issues surrounding recommender systems. &lt;/p&gt;
&lt;p&gt;Facebook &lt;em&gt;did&lt;/em&gt; give a talk on &lt;a href="https://research.fb.com/blog/2021/08/when-do-recommender-systems-amplify-user-preferences-a-theoretical-framework-and-mitigation-strategies/"&gt;mitigating long-term effects of recommenders&lt;/a&gt; during the conference (they concluded that even weak preferences for borderline content would inevitably be amplified and that the best you can do is try to slow this process down) but that&amp;rsquo;s not paper I want to talk about. &lt;/p&gt;
&lt;p&gt;This particular paper focuses on &lt;em&gt;scale&lt;/em&gt;, something Facebook knows better than almost anyone. In particular, how can recommendation models behind Facebook Marketplace be optimized to perform well for their first-order task (presumably engagement, conversion, etc.) while reducing latency and the computational cost of serving over one billion users per month?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The first step of the solution is the obvious one: cache recommendations from the &amp;ldquo;sophisticated and computationally expensive&amp;rdquo; ranking model to save on CPU cycles and latency that would result from online inference. &lt;/li&gt;
&lt;li&gt;The world and the user are always changing, so a lighter-weight &amp;ldquo;adjuster model&amp;rdquo; corrects for changes in user and item features since cache time. The adjuster model is distilled from the full ranker but takes into consideration a) the latest features, b) the &amp;ldquo;gap time&amp;rdquo; between caching and the present, and c) deltas in important features between caching time and present.&lt;/li&gt;
&lt;li&gt;Lastly, pre-fetch recommendations to device upon opening the Facebook app (but &lt;em&gt;before&lt;/em&gt; navigating to the Marketplace pane). In particular, results are only pre-fetched if indicated by the results of &lt;em&gt;two more models&lt;/em&gt;: one which estimates the probability of opening marketplace, and another model which estimates latency of generating the rankings.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All in all, it&amp;rsquo;s eye-opening to see the amount of engineering that goes into delivering computationally-intensive recommendations at massive scale.&lt;/p&gt;
&lt;h3&gt;Recommendations at a reasonable scale in a (mostly) serverless and open stack&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Presented by Jacopo Tagliabue from Coveo Labs. Paper and video at &lt;a href="https://dl.acm.org/doi/10.1145/3460231.3474604"&gt;ACM&lt;/a&gt;, code on &lt;a href="https://github.com/jacopotagliabue/you-dont-need-a-bigger-boat"&gt;Github&lt;/a&gt;, a similar talk from the same author on &lt;a href="https://www.youtube.com/watch?v=Ndxpo4PeEms"&gt;YouTube&lt;/a&gt;, and a similar blog post from the same author on &lt;a href="https://towardsdatascience.com/mlops-without-much-ops-d17f502f76e8"&gt;Towards Data Science&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;div class="image"&gt;
&lt;img src="https://danturkel.com/2021/12/06/coveo-recsys-architecture.jpg" alt="A diagram of Coveo Labs' architecture for a reasonably-scaled recommender system."&gt;
&lt;/div&gt;

&lt;p&gt;We just saw how much technical muscle Facebook throws at their scale issues, but the chances are that you&amp;rsquo;re not Facebook, and you don&amp;rsquo;t need solutions which scale to billions of users.&lt;/p&gt;
&lt;p&gt;Tagliabue defines &amp;ldquo;reasonable scale&amp;rdquo; as companies with a finite commodity cloud budget, dozens (not hundreds) of engineers, making 9 (not 10) figures per year, with terabyte (not petabyte) scale data. If this sounds like you, then the talk&amp;rsquo;s &lt;a href="https://github.com/jacopotagliabue/you-dont-need-a-bigger-boat"&gt;accompanying repo&lt;/a&gt; offers an architecture for a non-trivial recommender system, including data validation, deployment, monitoring, and orchestration, using open-source tools like &lt;a href="https://www.getdbt.com/"&gt;dbt&lt;/a&gt;, &lt;a href="https://metaflow.org/"&gt;Metaflow&lt;/a&gt;, and so on.&lt;/p&gt;
&lt;p&gt;Regardless of whether you intend to use any of the architecture, Tagliabue&amp;rsquo;s principles are valuable. He stresses the importance of data quality and leveraging the many high-quality SaaS/PaaS offerings so you can avoid time-consuming infrastructure maintenance.&lt;/p&gt;
&lt;h3&gt;RecSysOps: Best practices for operating a large-scale recommender system&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Presented by Ehsan Saberian from Netflix. Find the paper at &lt;a href="https://dl.acm.org/doi/fullHtml/10.1145/3460231.3474620"&gt;ACM&lt;/a&gt; and a video of the talk on &lt;a href="https://www.youtube.com/watch?v=VTLwSapTjVs"&gt;YouTube&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This talk was one of a few that stressed the importance of having a good ops backbone when building and maintaining recommender systems. &amp;ldquo;RecSysOps&amp;rdquo; is broken into issue detection, prediction, diagnosis, and resolution, all of which are easier said than done.&lt;/p&gt;
&lt;p&gt;I particularly liked that a team could take on these suggestions in an iterative approach. For starters, implement best practices and end-to-end monitoring, with a focus on the types of issues that are important to your stakeholders. Once issue detection is (relatively) under control, you might try your hand at predicting future issues.&lt;/p&gt;
&lt;p&gt;My favorite piece of advice was: &amp;ldquo;with every issue, make your RecSysOps better.&amp;rdquo; In other words, if detection was hard for this type of issue, try to predict it in the future. If resolution was hard, build a tool to automate the solution in the future.&lt;/p&gt;
&lt;h2&gt;Three talks on organizational issues&lt;/h2&gt;
&lt;p&gt;If &amp;ldquo;RecSysOps&amp;rdquo; is about the software practices which support recommender systems, these two talks are about the communication and cultural practices which support doing data science in a large organization:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dl.acm.org/doi/10.1145/3460231.3474616"&gt;Scaling enterprise recommender systems for decentralization&lt;/a&gt; by Maurits van der Goes, Heineken. The &amp;ldquo;decentralization&amp;rdquo; in this case is not about distributed systems or cloud architectures, it&amp;rsquo;s about decentralized organizations and how to build valuable ML infrastructure for a large company with heterogenous needs.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dl.acm.org/doi/abs/10.1145/3460231.3474618"&gt;Challenges experienced in public service media recommendation systems&lt;/a&gt;, presented by Andreas Grün for ZDF. Grün discusses the need to meet different teams within the organization where they were, exposing the appropriate abstractions and levels of control. He also brought up a topic rarely addressed at the conference: reducing energy consumption by (for example) replacing deep sequential recommenders with heuristics.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dl.acm.org/doi/10.1145/3460231.3474614"&gt;Building public service recommenders&lt;/a&gt;, presented by Christina Boididou for the BBC. Another great talk about recommenders in the public service sector, with insight on the organizational, infrastructural, and &lt;em&gt;cultural&lt;/em&gt; challenges of brinigng recommendations to a 99-year-old organization which previously relied on editorial curation.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Algorithmic advances&lt;/h2&gt;
&lt;p&gt;These talks are focused more on new ideas, methods, and algorithms, as well as new perspectives on issues related to recommender systems.&lt;/p&gt;
&lt;div class="image"&gt;
&lt;img src="https://danturkel.com/2021/12/06/roeiboot-op-zee-de-groot.jpg" alt="A 19th-century watercolor painting of a rowboat at sea by Frans Arnold Breuhaus de Groot."&gt;
&lt;p&gt;&lt;em&gt;Roeiboot op zee&lt;/em&gt; (19th century) by Frans Arnold Breuhaus de Groot, &lt;a href="https://www.rijksmuseum.nl/en/collection/RP-T-1921-352"&gt;via the Rijksmuseum&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;

&lt;h3&gt;Top-&lt;em&gt;k&lt;/em&gt; contextual bandits with equity of exposure&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Presented by Oliver Jeunen for University of Antwerp. Paper at &lt;a href="https://dl.acm.org/doi/10.1145/3460231.3474248"&gt;ACM&lt;/a&gt; and &lt;a href="http://adrem.uantwerpen.be/bibrem/pubs/JeunenRecSys2021_B.pdf"&gt;the author&amp;rsquo;s website&lt;/a&gt;, code on &lt;a href="https://github.com/olivierjeunen/EARS-recsys-2021"&gt;Github&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Olivier Jeunen and Bart Goethals won the Best Student Paper award for their &lt;em&gt;other&lt;/em&gt; RecSys paper &lt;a href="https://dl.acm.org/doi/10.1145/3460231.3474247"&gt;Pessimistic reward models for off-policy learning in recommendation&lt;/a&gt;, but I particularly liked this one on bandits with &lt;a href="https://arxiv.org/abs/2004.13157"&gt;equitable exposure&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The key idea is that a small difference in the computed relevance between two items might mean a big difference in exposure if one item gets knocked out of the top &lt;em&gt;k&lt;/em&gt; items shown in a module. You may have any number of reasons for wanting to ensure equitable exposure among your item population, which makes this a potential issue.&lt;/p&gt;
&lt;p&gt;The paper&amp;rsquo;s exposure-aware arm selection (EARS) algorithm attempts to ensure that a probability of an item being clicked is proportional to its relevance and not unduly confounded by extraneous exposure effects. The math gets a bit above my pay-grade, but the experimental results on &lt;a href="https://github.com/deezer/carousel_bandits"&gt;Deezer&amp;rsquo;s carousel dataset&lt;/a&gt; suggested that EARS improves equitable exposure with minimal impacts to expected reward. There&amp;rsquo;s even a hyperparameter to control the fairness-reward trade-off.&lt;/p&gt;
&lt;h3&gt;Cold start similar artists ranking with gravity-inspired graph autoencoders&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Presented by Guillaume Salha-Galven from Deezer. Paper at &lt;a href="https://dl.acm.org/doi/10.1145/3460231.3474252"&gt;ACM&lt;/a&gt; and &lt;a href="https://arxiv.org/abs/1905.09570"&gt;Arxiv&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This paper tackles the task of generating a &amp;ldquo;similar artists&amp;rdquo; module for new artists on &lt;a href="https://www.deezer.com/"&gt;Deezer&lt;/a&gt;, who have metadata but few streams. &lt;/p&gt;
&lt;p&gt;The authors formulate the problem as a weighted, directed graph of top-&lt;em&gt;k&lt;/em&gt; similar artists, and they want to predict edges linking to the cold-start nodes. A graph autoencoder is used to learn graph embeddings from the topology of the graph and the artist features, but graph autoencoders are typically for undirected graphs, learning a metric space where &lt;em&gt;d(x,y)=d(y,x)&lt;/em&gt; for any pair of items. &lt;/p&gt;
&lt;p&gt;To adapt the method for their directed graph, they learn a vector and a &lt;em&gt;mass&lt;/em&gt; for each node, where directed edges are modeled as accelerations due to &amp;ldquo;gravity&amp;rdquo; and popular artists end up having larger mass. This makes sense: the Beatles might be a top similar artist for many artists, but there&amp;rsquo;s a high bar to make it into the similar artists list for the Beatles. I thought this was a particularly intuitive and clever solution and according to the paper it beat a long list of baseline approaches.&lt;/p&gt;
&lt;h3&gt;Reverse maximum inner product search&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Presented by Daichi Amagata from Osaka University. Paper at &lt;a href="https://dl.acm.org/doi/10.1145/3460231.3474229"&gt;ACM&lt;/a&gt; and &lt;a href="https://arxiv.org/abs/2110.07131"&gt;Arxiv&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Many recommender systems are variants of &lt;a href="https://en.wikipedia.org/wiki/Matrix_factorization_(recommender_systems)"&gt;matrix factorization&lt;/a&gt; or other factorized models: you learn a vector for the user, a vector for the item, and the score for that pair is the dot-product of those vectors. We can take a user vector, multiply it by the entire item matrix, and rank the scores to get an ordered list of recommendations for that user.&lt;/p&gt;
&lt;p&gt;But what if you&amp;rsquo;ve got a new item vector and you want to know which users would have that in their top &lt;em&gt;k&lt;/em&gt; recommendations (to know how well a product might perform, or who to market it to)? You could generate the top recommendations for every user, but that&amp;rsquo;s expensive. This is the gist of the reverse maximum inner product search problem.&lt;/p&gt;
&lt;p&gt;The paper proposes an algorithm for doing this quite quickly, without any approximation. I&amp;rsquo;ll leave the heavy-lifting to the paper, but the trick is to avoid computing the full top-&lt;em&gt;k&lt;/em&gt; result set by building a data structure to support computing upper- and lower-bounds on the &lt;em&gt;k&lt;/em&gt;th largest inner product for a user. The end result is miles faster than any other exact solution&amp;hellip;mostly because the alternatives all involve solving the (forward) maximum inner product search problem for every user.&lt;/p&gt;
&lt;p&gt;Given the prevalence of factorized models for recommendation, this seems like a very promising tool to have.&lt;/p&gt;
&lt;h3&gt;Transformers4Rec&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Presented by Gabriel de Souza Pereira Moreira from NVIDIA, with co-authors from NVIDIA and Facebook. Paper at &lt;a href="https://dl.acm.org/doi/10.1145/3460231.3474255"&gt;ACM&lt;/a&gt; and &lt;a href="https://research.fb.com/publications/transformers4rec-bridging-the-gap-between-nlp-and-sequential-session-based-recommendation/"&gt;Facebook Research&lt;/a&gt;, with a blog post on &lt;a href="https://medium.com/nvidia-merlin/transformers4rec-4523cc7d8fa8"&gt;Medium&lt;/a&gt; and code on &lt;a href="https://github.com/NVIDIA-Merlin/Transformers4Rec"&gt;Github&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I am not an expert in NLP and I will not attempt to explain attention and transformers to you (though &lt;a href="https://jalammar.github.io/illustrated-transformer/"&gt;here&amp;rsquo;s a good walkthrough&lt;/a&gt; if you want one). If you&amp;rsquo;ve heard folks talking about the incredible things that &lt;a href="https://en.wikipedia.org/wiki/BERT_(language_model)"&gt;BERT&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/GPT-3"&gt;GPT-3&lt;/a&gt; can do, well the T in those names stands for transformers.&lt;/p&gt;
&lt;p&gt;Transformers operate on sequential data, which is a natural fit for language (i.e. sequences of words), but has now been adapted for sequential recommendation contexts: given a sequence of user-item interactions, predict the &lt;em&gt;next&lt;/em&gt; item that the user will interact with. Since transformers have quickly become the de facto backbone of the latest state of the art in language models, we shouldn&amp;rsquo;t be surprised to find that this paper boasts wins in the &lt;a href="https://developer.nvidia.com/blog/how-to-build-a-winning-deep-learning-powered-recommender-system-part-3/"&gt;WSDM&lt;/a&gt; and &lt;a href="https://medium.com/nvidia-merlin/winning-the-sigir-ecommerce-challenge-on-session-based-recommendation-with-transformers-v2-793f6fac2994"&gt;SIGIR&lt;/a&gt; recommendation challenges.&lt;/p&gt;
&lt;p&gt;Given the model performance, the pedigree of the NVIDIA and Facebook, and the fact that they offer Tensorflow &lt;em&gt;and&lt;/em&gt; PyTorch implementations, I suspect that we&amp;rsquo;ll be seeing a lot of Transformers4Rec in new sequential recommenders and various deep learning architectures for recommendation.&lt;/p&gt;
&lt;h2&gt;Two talks on theory&lt;/h2&gt;
&lt;p&gt;Two of my favorite talks were both presented by Minmin Chen, from the &lt;a href="https://research.google/teams/brain/"&gt;Google Brain&lt;/a&gt; team.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The first talk creates a foundational framework for discussing &lt;a href="https://dl.acm.org/doi/10.1145/3460231.3474601"&gt;exploration in recommender systems&lt;/a&gt; (which is also the name of the paper). Chen breaks this down into system exploration (surfacing tail content content to benefit content providers and to improve the model), user exploration (discovering &lt;a href="https://eugeneyan.com/writing/serendipity-and-accuracy-in-recommender-systems/"&gt;serendipitous&lt;/a&gt; content for users), and online exploration (reducing model uncertainty on rarely seen items without hurting the user), and she offers ideas on how to measure each of these facets.&lt;/li&gt;
&lt;li&gt;The second talk, &lt;a href="https://dl.acm.org/doi/10.1145/3460231.3474236"&gt;Values of user exploration in recommender systems&lt;/a&gt;, elaborates on the user exploration facet. The authors performed offline and online tests on several &lt;a href="https://medium.com/intro-to-artificial-intelligence/reinforce-a-policy-gradient-based-reinforcement-learning-algorithm-84bde440c816"&gt;REINFORCE&lt;/a&gt;-based models which emphasize exploration and measured results for accuracy, diversity, novelty, and serendipity. The paper does a great job building on the principles laid out in the previous paper and putting them to work by demonstrating value on real users.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Other talks of note&lt;/h2&gt;
&lt;p&gt;At the risk of outlining every single talk from the conference, below I list just a few more talks that all deserve a look for one reason or another.&lt;/p&gt;
&lt;div class="image"&gt;
&lt;img src="https://danturkel.com/2021/12/06/explainable-item-set-recommendation.png" alt="A slide demonstrating a module of similar items with recommendation explanations drawn from attributes of the items."&gt;
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dl.acm.org/doi/10.1145/3460231.3474240"&gt;Explainable attribute-aware item-set recommendations&lt;/a&gt; from Yang Li et al at Amazon. This paper shows off a model which generates a module of similar recommended items alongside relevant attributes that the user might want to compare between them (see image above). It&amp;rsquo;s one thing to hand-pick the attributes to show if you&amp;rsquo;re a small retailer with only a few types of goods, but Amazon needs a solution that can adapt to basically any type of product. &lt;em&gt;Also available from &lt;a href="https://assets.amazon.science/d3/ad/9af131bd49b8a0697c6bd763a1cf/ex3-explainable-attribute-aware-item-set-recommendations.pdf"&gt;Amazon&lt;/a&gt;.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dl.acm.org/doi/10.1145/3460231.3474267"&gt;Recommendation on live-streaming platforms: Dynamic availability and repeat consumption&lt;/a&gt; from Jérémie Rappaz and his colleagues at EPFL and UC San Diego. Live-streaming platforms like Twitch are becoming one of the largest sources of video content, and this paper discusses some of the unique challenges they pose for recommendation. In particular, recommenders must learn to recommend intelligently among the streamers &lt;em&gt;currently broadcasting&lt;/em&gt; and understand dependencies between them. &lt;em&gt;Also on the &lt;a href="https://cseweb.ucsd.edu/~jmcauley/pdfs/recsys21b.pdf"&gt;author&amp;rsquo;s website&lt;/a&gt;.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;I&amp;rsquo;ve seen a lot of talk about &lt;a href="https://dl.acm.org/doi/10.1145/3460231.3474273"&gt;Negative interactions for improved collaborative filtering: Don&amp;rsquo;t go deeper, go higher&lt;/a&gt; from Harold Steck and Dawen Liang from Netflix. The paper contributes to the &lt;a href="https://arxiv.org/abs/1907.06902"&gt;ongoing conversation&lt;/a&gt; around the extent to which deep learning has been or will be useful for recommendation (as did &lt;a href="https://dl.acm.org/doi/abs/10.1145/3460231.3475944"&gt;another paper&lt;/a&gt; this year). I don&amp;rsquo;t have a horse in that race, but I found the choice of model inputs (using a user&amp;rsquo;s previous items &lt;em&gt;and&lt;/em&gt; item pairs as inputs) clever, and I particularly like the fact that they focused on inputs that led to &lt;em&gt;negative&lt;/em&gt; scores—learning these types of anti-recommendations is often difficult for many traditional models.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dl.acm.org/doi/10.1145/3460231.3474242"&gt;Page-level optimization of e-commerce item recommendations&lt;/a&gt; from Chieh Lo et al from eBay. I mostly like this paper because it focuses on an atypical recommendation setting: personalizing the modules on an item display page. The page is even modeled as a sequence of modules with an RNN. The paper&amp;rsquo;s got offline and online experiments, a detailed end-to-end architecture, and real-world results: 2.48% increase in click-through and 7.34% increase in purchase-through. &lt;em&gt;Also on &lt;a href="https://arxiv.org/abs/2108.05891"&gt;Arxiv&lt;/a&gt;.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dl.acm.org/doi/10.1145/3460231.3474250"&gt;Burst-induced multi-armed bandit for learning recommendation&lt;/a&gt; from Rodrigo Alves et al at TU Kaiserlautern. This paper adds a neat twist to the context-free &lt;a href="https://en.wikipedia.org/wiki/Multi-armed_bandit"&gt;multi-armed bandit&lt;/a&gt; by accounting for &amp;ldquo;bursty&amp;rdquo; activity (e.g. tons of searches for David Bowie after his death). The bandit has a stationary distribution it updates for ordinary activity, but switches to modeling a separate burst distribution when unusually high activity is detected.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I loved getting to hear a wide variety of perspectives on issues in recommendation and I&amp;rsquo;m looking forward to attending next year, ideally in person.&lt;/p&gt;</content><category term="misc"></category><category term="conference"></category><category term="recommender-systems"></category></entry><entry><title>Watch this space</title><link href="https://danturkel.com/2021/06/01/watch-this-space.html" rel="alternate"></link><published>2021-06-01T00:00:00-04:00</published><updated>2021-06-01T00:00:00-04:00</updated><author><name>Dan Turkel</name></author><id>tag:danturkel.com,2021-06-01:/2021/06/01/watch-this-space.html</id><summary type="html">&lt;div class="image"&gt;
&lt;img src="https://danturkel.com/2021/06/01/eye-of-maria-miles-heyward.jpg" alt="A watercolor painting of an eye on an ivory ring, by Edward Greene Malbone."&gt;
&lt;p&gt;&lt;em&gt;Eye of Maria Miles Heyward&lt;/em&gt; (c. 1802) by Edward Greene Malbone, &lt;a href="https://www.metmuseum.org/art/collection/search/20414"&gt;via the Metropolitan Museum of Art&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;Pretty soon I&amp;rsquo;ll be launching a new blog around these parts, so keep your eyes peeled. If you want to make sure you don&amp;rsquo;t miss out, feel free to subscribe to …&lt;/p&gt;</summary><content type="html">&lt;div class="image"&gt;
&lt;img src="https://danturkel.com/2021/06/01/eye-of-maria-miles-heyward.jpg" alt="A watercolor painting of an eye on an ivory ring, by Edward Greene Malbone."&gt;
&lt;p&gt;&lt;em&gt;Eye of Maria Miles Heyward&lt;/em&gt; (c. 1802) by Edward Greene Malbone, &lt;a href="https://www.metmuseum.org/art/collection/search/20414"&gt;via the Metropolitan Museum of Art&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;Pretty soon I&amp;rsquo;ll be launching a new blog around these parts, so keep your eyes peeled. If you want to make sure you don&amp;rsquo;t miss out, feel free to subscribe to the &lt;a href="/feeds/rss.xml"&gt;RSS feed&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;See you soon.&lt;/p&gt;</content><category term="misc"></category><category term="meta"></category></entry></feed>