-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathambigrams.html
169 lines (155 loc) · 8.24 KB
/
ambigrams.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
<!DOCTYPE html>
<html>
<head>
<link rel="canonical" href="https://hardmath123.github.io/ambigrams.html"/>
<link rel="stylesheet" type="text/css" href="/static/base.css"/>
<title>Words that do Handstands - Comfortably Numbered</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<link rel="alternate" type="application/rss+xml" title="Comfortably Numbered" href="/feed.xml" />
<!--
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script>
MathJax.Hub.Config({
tex2jax: {inlineMath: [['$','$']]}
});
</script>
-->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.css" integrity="sha384-Um5gpz1odJg5Z4HAmzPtgZKdTBHZdw8S29IecapCSB31ligYPhHQZMIlWLYQGVoc" crossorigin="anonymous">
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.js" integrity="sha384-YNHdsYkH6gMx9y3mRkmcJ2mFUjTd0qNQQvY9VYZgQd7DcN7env35GzlmFaZ23JGp" crossorigin="anonymous"></script>
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/contrib/auto-render.min.js" integrity="sha384-vZTG03m+2yp6N6BNi5iM4rW4oIwk5DfcNdFfxkk9ZWpDriOkXX8voJBFrAO7MpVl" crossorigin="anonymous"></script>
<script>
document.addEventListener("DOMContentLoaded", function() {
renderMathInElement(document.body, {
// customised options
// • auto-render specific keys, e.g.:
delimiters: [
{left: '$$', right: '$$', display: true},
{left: '$', right: '$', display: false},
{left: '\\begin{align}', right: '\\end{align}', display: true},
{left: '\\(', right: '\\)', display: false},
{left: '\\[', right: '\\]', display: true}
],
// • rendering keys, e.g.:
throwOnError : false
});
});
</script>
</head>
<body>
<header id="header">
<script src="static/main.js"></script>
<div>
<a href="/"><span class="left-word">Comfortably</span> <span class="right-word">Numbered</span></a>
</div>
</header>
<article id="postcontent" class="centered">
<section>
<h1>Words that do Handstands</h1>
<center><em><p>Dreaming ambigrams by gradient descent</p>
</em></center>
<h4>Monday, August 26, 2019 · 2 min read</h4>
<p>The other day I ordered a book on art history from Amazon… which reminded me
of the movie <em>Angels and Demons</em>, which reminded me of ambigrams. Ambigrams are
those stylized words that look the same when you turn them upside down. Here is
the classic, <a href="http://www.johnlangdon.net/works/angels-demons/">by John
Langdon</a>.</p>
<p><img src="static/ambigrams/langdon.png" alt="earth air fire water"></p>
<p>I’ve been thinking a lot about the absurd power of gradient descent these days,
and so almost immediately I felt the need to try and synthesize ambigrams
on-demand by gradient descent.</p>
<p>It turns out that this isn’t too hard to implement. Let’s start with just
single-character ambigrams. Here’s the plan: first, we train some sort of
neural net to recognize characters. This training is by gradient descent on the
weights / convolution filter / whatnot; specifically, we imagine a function of
the weights that outputs some loss and take its derivative <em>with respect to the
weights</em> to do gradient descent. Once the neural net is trained, however, we
can just treat it as a single function from tensors (input image) to tensors (a
one-hot encoding of the classification… or some softmaxed approximation).
Taking that one-hot output and converting it into a cross-entropy loss against
some desired output, we can now do gradient descent to minimize this loss <em>with
respect to the input image</em> (keeping the weights constant). This process lets
us “dream” of images representing whichever characters we want.</p>
<p>Similarly, we can do the same thing but with the tensor turned upside-down, to
dream images of upside-down characters. Okay, so now we can just combine these
two processes — literally, by adding the loss functions — to dream an image
that matches one character right-side-up and another character upside-down.
That’s it! Now we let it run all day to generate an image for each alphanumeric
character pair (36^2 = 1296) and we start to get a nice little “ambigram font”:</p>
<p><img src="static/ambigrams/grid.png" alt="grid"></p>
<p>Combining letters from this “font” lets us make arbitrary ambigrams.</p>
<p>This was a low-effort operation, hacked together over an evening and a morning,
plus a full day of “training.” I’m sure it’s possible to do better. Here are
some technical notes for anyone who wants to have a go at it. My code is all
online, <a href="https://github.com/kach/neural-ambigrams">here</a>.</p>
<ul>
<li>It’s all written in PyTorch. The neural net itself is literally PyTorch’s
built-in <a href="https://github.com/pytorch/examples/tree/master/mnist">MNIST
example</a>.</li>
<li>I used the <a href="https://www.nist.gov/node/1298471/emnist-dataset">EMNIST</a>
dataset, which is MNIST that also adds in the alphabets from the NIST
database (and comes with PyTorch). I used the “balanced” split.</li>
<li>I added an L1 regularizer to force the output images to be sparse, i.e.
mostly “blank paper” with some “ink.” Without this, you get very dark images.</li>
</ul>
<p>Okay, now time for some pictures. Some turned out better than others, and there
is plenty of scope for improvement…</p>
<p>P.S. Depending on your browser, images in this post may or may not flip
upside-down automatically if you hover over them.</p>
<style>
img {
transform: rotate(0deg);
transition: transform 0.5s;
}
img:hover {
transform: rotate(180deg);
transition: transform 0.5s;
}
</style>
<h2 id="angel-and-demon">ANGEL AND DEMON</h2>
<p><img src="static/ambigrams/angel-and-demon.png" alt="ANGEL AND DEMON"></p>
<h2 id="banana">BANANA</h2>
<p><img src="static/ambigrams/banana.png" alt="BANANA"></p>
<h2 id="year-2016">YEAR, 2016</h2>
<p><img src="static/ambigrams/year-2016.png" alt="YEAR, 2016"></p>
<h2 id="good-evil">GOOD, EVIL</h2>
<p><img src="static/ambigrams/good-evil.png" alt="GOOD, EVIL"></p>
<h2 id="teamwork">TEAMWORK</h2>
<p><img src="static/ambigrams/teamwork.png" alt="TEAMWORK"></p>
<h2 id="blues-skate">BLUES, SKATE</h2>
<p><img src="static/ambigrams/blues-skate.png" alt="BLUES, SKATE"></p>
<h2 id="dream-world">DREAM, WORLD</h2>
<p><img src="static/ambigrams/dream-world.png" alt="DREAM, WORLD"></p>
<hr>
<p>Update (3/26/20): Alex Pietrow created <a href="https://makeambigrams.com/ambigram-generator/">a
website</a> that lets you use this
font to build your own ambigrams (along with more ambigram fun).</p>
</section>
<div id="comment-breaker">◊ ◊ ◊</div>
</article>
<footer id="footer">
<div>
<ul>
<li><a href="https://github.com/kach">
Github</a></li>
<li><a href="feed.xml">
Subscribe (RSS feed)</a></li>
<li><a href="https://twitter.com/hardmath123">
Twitter</a></li>
<li><a href="https://creativecommons.org/licenses/by-nc/3.0/deed.en_US">
CC BY-NC 3.0</a></li>
</ul>
</div>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-46120535-1', 'hardmath123.github.io');
ga('require', 'displayfeatures');
ga('send', 'pageview');
</script>
</footer>
</body>
</html>