<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Omar Hussein on Medium]]></title>
        <description><![CDATA[Stories by Omar Hussein on Medium]]></description>
        <link>https://medium.com/@omar-hussein?source=rss-8fb24e986396------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*SO9LIg04YF51VUEKwNPBYw.jpeg</url>
            <title>Stories by Omar Hussein on Medium</title>
            <link>https://medium.com/@omar-hussein?source=rss-8fb24e986396------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 14 Nov 2026 01:16:23 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@omar-hussein/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[You Don’t Have to Hand Over Your Privacy to Block Porn]]></title>
            <link>https://omar-hussein.medium.com/you-dont-have-to-hand-over-your-privacy-to-block-porn-642d88df4e63?source=rss-8fb24e986396------2</link>
            <guid isPermaLink="false">https://medium.com/p/642d88df4e63</guid>
            <category><![CDATA[pornography]]></category>
            <category><![CDATA[odysseus]]></category>
            <category><![CDATA[claude-code]]></category>
            <category><![CDATA[porn-addiction]]></category>
            <dc:creator><![CDATA[Omar Hussein]]></dc:creator>
            <pubDate>Wed, 05 Aug 2026 21:23:53 GMT</pubDate>
            <atom:updated>2026-08-05T21:29:15.144Z</atom:updated>
            <content:encoded><![CDATA[<p>Let’s start with the honest version of the problem.</p><p>Porn is now closer than almost anything else you might want. It’s one tap from a locked phone, one autocomplete from an address bar, one algorithmic slip inside an app you opened for a completely different reason. For a lot of people this stopped being a moral question a while ago and became a <strong>logistics</strong> question: the friction is gone, so the habit runs on rails and it’s the easiest sneakiest addiction you will ever see. So if you want to protect yourself, your kids, your family your friend this doc is for you. Now if this is for you then think of this as the Odysseus rule is if you can’t control yourself then prevent yourself</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/928/1*eNbY3K9LEj_GoG4hqCmnhg.png" /></figure><p>“a choice you make now to lock in a safe decision and stop future temptations. It comes from the <a href="https://en.wikipedia.org/wiki/Ulysses_pact">The Odyssey</a> myth where Odysseus tied himself to a ship mast to safely hear the deadly Sirens.”</p><p>Fyi, If you don’t care to read the entire thing just Cmd+A then Paste it into Claude code and tell it to do it for you. But for your phone you have to do it manually and It is NOT THAT hard ~ Just do it.</p><p>And when you go looking for help, here’s what the market offers you:</p><ul><li><strong>Accountability apps</strong> that monitor your screen activity and report it to another person. Some capture screenshots at intervals. To work at all, they need deep, persistent access to everything you look at.</li><li><strong>Filtering apps</strong> that install a VPN or device-management profile, then route your entire internet connection through their servers so they can inspect it.</li><li><strong>Subscriptions</strong>, usually $10–$20 a month, forever.</li></ul><p>I want to be fair to these tools. For some people — particularly people who genuinely want a human accountability partner — they’re the right call, and the trade is one they’ve made with their eyes open.</p><p>But it <em>is</em> a trade. You are handing a company continuous visibility into your browsing, your screen, or both, and paying for the privilege, to solve a problem your operating system can already solve for free. For a lot of people that’s not a trade worth making — and worse, the privacy cost becomes the reason they never set anything up at all.</p><p>There’s another path. Everything below runs entirely on your own machine. Nothing is sent anywhere. Nothing is monitored. There’s no account, no subscription, and no third party who can see what you do.</p><p>The only catch is that it involves the terminal — which is exactly where Claude Code comes in.</p><h3>The shortcut: let Claude Code do it</h3><p>If you’re not technical, this section is the whole article. You do not need to understand any of the commands that follow.</p><p><strong>Claude Code</strong> is Anthropic’s coding assistant that runs in your terminal. It reads files, writes files, and runs commands on your machine — with your approval at each step. Which means you can hand it a technical guide and say “do this,” and it will.</p><h3>Setup, once</h3><p>Install it — on macOS, open <strong>Terminal</strong> (Cmd+Space, type “Terminal”) and paste:</p><pre>curl -fsSL https://claude.ai/install.sh | bash</pre><p>Then start it:</p><pre>claude</pre><p>The first run opens your browser to log in. You’ll need a Claude Pro, Max, Team, or Enterprise plan — the free tier doesn’t include Claude Code. (Homebrew users: brew install --cask claude-code works too. Full install docs: <a href="https://code.claude.com/docs/en/setup">code.claude.com/docs/en/setup</a>.)</p><h3>Then just ask</h3><p>It will read the guide, write the script, ask your permission before touching anything in /etc, install it, apply the block, and check its own work. When it needs sudo, it&#39;ll tell you and wait.</p><p>That last one matters. Ask it. You should always understand what’s been changed on a machine you own, and Claude Code will walk you through it in plain language.</p><p><strong>A note on trust:</strong> Claude Code runs locally and asks before it does anything consequential. But you’re still giving an AI assistant permission to modify system files — read what it proposes, and don’t blanket-approve. The whole point of this approach is staying in control of your own machine.</p><h3>What you’re actually building, in plain English</h3><p>Most blocking guides hand you one technique, which is why so many blocks quietly fail. A web request passes through several independent stages, and a block at one stage does nothing about the others.</p><p>Here’s the path a request actually takes:</p><p>Stage What can block here <strong>The app or browser</strong> Screen Time, browser policy. Native apps ignore web filters entirely. <strong>The browser’s own DNS</strong> Nothing — this <em>skips</em> the next two stages when enabled. So does a browser VPN. <strong>The OS resolver</strong> /etc/hosts — covers every account and every app on the machine. <strong>Your DNS server</strong> A filtering resolver — whole categories, maintained by someone else. <strong>Your router</strong> Covers every device on the Wi-Fi. Phones, laptops, TVs, guests.</p><p>Read that top to bottom and the strategy writes itself:</p><ul><li>A <strong>hosts file</strong> is precise but local and manual — good for specific named sites.</li><li>A <strong>filtering resolver</strong> is broad and continuously maintained — good for a whole category you could never enumerate by hand.</li><li>Neither survives a browser VPN.</li></ul><p>So you use more than one. That’s the entire idea.</p><h3>The mistake that makes a block look like it works</h3><p>This is the part that costs people an afternoon, and it’s the reason the guide below exists at all.</p><p>Your first attempt sinks each domain to a dead IPv4 address:</p><pre>0.0.0.0	reddit.com</pre><p>You apply it. The file looks right. And the site still loads.</p><p>So you blame the browser. You clear caches. You try a different browser. It still loads.</p><p>The browser was never the problem. Here’s what the system resolver actually returned with a correct-looking, IPv4-only block in place:</p><p>Domain Resolved to Result <a href="http://www.reddit.com/">www.reddit.com</a> 0.0.0.0 blocked reddit.com 2a04:4e42:400::396 <strong>still reachable</strong> redgifs.com 2a06:98c1:310a::6812:247b <strong>still reachable</strong> i.redd.it 2a04:4e42:3d::396 <strong>still reachable</strong> pornhub.com 0.0.0.0 blocked</p><p>An entry pointing at 0.0.0.0 answers the <strong>IPv4 question only</strong>. When a domain also publishes an IPv6 (AAAA) record, macOS goes out to real DNS for the IPv6 address, gets a live one, and connects over IPv6 — which it <em>prefers</em> when available.</p><p>Your block is bypassed by the operating system itself, silently.</p><p>Notice how convincing the failure is. Domains without IPv6 block perfectly, so the file appears to work. Whether any given site leaks depends on whether it publishes an AAAA record — which is why half your list can look fine while the half you actually care about doesn’t.</p><p>The fix is to sink both address families. Every domain needs two lines:</p><pre>0.0.0.0	reddit.com<br>::	reddit.com<br>0.0.0.0	www.reddit.com<br>::	www.reddit.com</pre><p>:: is the IPv6 equivalent of 0.0.0.0. The script in the appendix emits both automatically, which is why it writes four lines per domain you add.</p><p><strong>This single detail is why a hand-rolled block fails and a commercial app appears to succeed.</strong> It has nothing to do with the app being better. It has to do with two lines instead of one.</p><h3>Never verify a block in a browser</h3><p>Testing by visiting the site is how the IPv6 leak hides for so long, because a browser tells you <em>that</em> something loaded, never <em>why</em>. Ask the resolver directly instead:</p><pre>dscacheutil -q host -a name reddit.com</pre><p>You want 0.0.0.0 and nothing else. <strong>Any </strong><strong>ipv6_address line in that output is a leak</strong>, no matter how correct your hosts file looks.</p><p>Loop over your whole list and check every entry. If you take one habit from this article, take that one. (Or just tell Claude Code: <em>“verify every domain on my blocklist at the resolver level and flag any IPv6 leaks.”</em>)</p><h3>Two more limits nobody mentions</h3><p><strong>There are no wildcards.</strong> You cannot write *.reddit.com. Every subdomain must be named, and any you miss stays open — including the media subdomains that serve the actual images.</p><p><strong>Apps use different domains than websites.</strong> Reddit’s mobile and desktop apps talk to oauth.reddit.com and gateway.reddit.com. Block only reddit.com and the website goes dark while the app keeps working perfectly.</p><p>A list that actually holds looks more like this:</p><pre>sudo blocksites add \<br>  reddit.com old.reddit.com new.reddit.com np.reddit.com \<br>  sh.reddit.com amp.reddit.com m.reddit.com out.reddit.com \<br>  oauth.reddit.com gateway.reddit.com api.reddit.com \<br>  redd.it i.redd.it v.redd.it preview.redd.it \<br>  external-preview.redd.it redditmedia.com redditstatic.com</pre><p>Working that out for each site is tedious and error-prone. It’s also exactly the kind of thing an AI assistant is good at: <em>“figure out every domain the X app and website use, and block all of them.”</em></p><h3>The category problem, and why a hand-written list can’t solve it</h3><p>Here’s where you have to be realistic with yourself.</p><p>A hosts file is exact. That’s its strength for a handful of named sites and its <strong>fatal weakness</strong> for a category. If your goal is “block adult sites” rather than “block these four sites,” a hand-written list is the wrong instrument entirely. There are millions of those domains, the large ones run dozens of mirror domains each, and new ones appear continuously.</p><p>A static list gives you the <em>feeling</em> of a block while leaving the door open.</p><p>The fix is a filtering DNS resolver — you point your machine at a DNS server that already refuses to resolve entire categories, maintained by someone else, updated constantly. Two free options:</p><p>Resolver Addresses Adult domains Everything else <strong>Cloudflare for Families</strong> 1.1.1.3 / 1.0.0.3 0.0.0.0 resolves normally <strong>CleanBrowsing Adult</strong> 185.228.168.10 NXDOMAIN resolves normally</p><p>Set it on every network service at once — and note the IPv6 addresses, for exactly the same reason they mattered above:</p><pre>sudo bash -c &#39;networksetup -listallnetworkservices | tail -n +2 | sed &quot;s/^\*//&quot; |<br>while IFS= read -r s; do<br>  networksetup -setdnsservers &quot;$s&quot; \<br>    1.1.1.3 1.0.0.3 2606:4700:4700::1113 2606:4700:4700::1003 &amp;&amp; echo &quot;set: $s&quot;<br>done&#39;</pre><p>This survives reboots and applies to every account on the machine.</p><p>Yes, you’re now using a third party — but only as a <em>resolver</em>, the same role your ISP already plays. Cloudflare’s family resolver sees DNS queries, which your ISP’s resolver sees anyway. Nobody is screenshotting your desktop.</p><p><strong>The division of labor:</strong> the resolver handles the category; the hosts file handles your specific named sites. Reddit resolves normally through both resolvers above, because it isn’t classified as adult. That’s not a bug — it’s why you do both layers.</p><h3>The bypasses you need to know about</h3><p>A guide that doesn’t tell you about these is selling you false confidence.</p><p><strong>Browsers with a built-in VPN.</strong> Opera and Opera GX ship a free one. Two clicks and browser traffic tunnels out through Opera’s servers — hosts file and filtering DNS both bypassed completely, nothing to install. No system-level configuration closes this. If the machine is meant to be genuinely restricted, that browser has to go.</p><p><strong>DNS-over-HTTPS.</strong> Chrome, Edge and Firefox can resolve domains themselves over HTTPS, skipping the OS resolver entirely — meaning both your hosts file <em>and</em> your DNS setting. Anyone can point Secure DNS at a non-filtering provider in settings and walk around the whole setup. For Chrome and Edge, the reliable fix is a managed policy setting DnsOverHttpsMode to off. (Firefox has a canary-domain escape hatch, but Cloudflare&#39;s family resolver doesn&#39;t trigger it — don&#39;t count on it.)</p><p><strong>Chrome holds connections open.</strong> A mundane one that wastes real debugging time: Chrome keeps its own DNS cache and reuses live sockets. It can keep loading a site well after the block is correctly in place. Quit it fully — Cmd+Q, not just closing the window — before concluding anything is broken.</p><h3>The iPhone, where none of this transfers</h3><p>iOS has no hosts file and no root access. The equivalent lives in <strong>Screen Time</strong>.</p><p><strong>Settings → Screen Time → Content &amp; Privacy Restrictions → Content Restrictions → Web Content → Limit Adult Websites.</strong></p><p>That gives you two things at once: Apple’s maintained category filter, and a <strong>Never Allow</strong> list underneath it for specific domains you add by hand — the same division of labor as on the Mac. It filters at the WebKit level, so it covers every iOS browser. There’s no browser-VPN-shaped hole.</p><p><strong>Lock it, or it’s decorative.</strong> Set a Screen Time passcode under <strong>Lock Screen Time Settings</strong>, and make it <em>different</em> from your device unlock passcode. Without that, the restrictions are two taps from being switched off by anyone holding the phone. Add a recovery Apple ID when prompted — otherwise forgetting this passcode means wiping the device.</p><p><strong>The gap that will bite you:</strong> web content filters do not touch native apps. The Reddit app talks to its API domains directly and sails past Web Content restrictions entirely. Delete the app, then set <strong>iTunes &amp; App Store Purchases → Installing Apps → Don’t Allow</strong> so it can’t just be reinstalled.</p><p><strong>DNS on iOS:</strong> setting DNS manually in Wi-Fi settings applies to that one network only and does nothing on cellular. Install a DNS configuration profile instead — Cloudflare publishes one for 1.1.1.1 for Families that covers Wi-Fi and cellular together.</p><h3>Doing it for a whole household</h3><p>If the goal is a family rather than one computer, the router is by far the best value for effort. Set its DNS servers to 1.1.1.3 and 1.0.0.3 in the admin page — usually at 192.168.1.1 — and every device that joins the Wi-Fi inherits the filter. Phones, laptops, tablets, TVs, guests. Nothing to install per device.</p><p>Its limit is precisely its scope: it stops at your front door. Cellular data and any other Wi-Fi network are unaffected.</p><h3>What this can and can’t do</h3><p>The failure mode of a blocking guide is that someone believes it more than they should. So:</p><p><strong>It stops casual and habitual access</strong>, which is usually the actual goal. Removing the two-second path to a site is most of the benefit for most people, and this setup does that thoroughly. Habits run on friction. Put friction back.</p><p><strong>It does not stop a motivated person with admin rights.</strong> Everything here is reversible by the same admin account that applied it — one command lifts the hosts block, one setting changes DNS back. If you’re blocking yourself, that’s fine; friction is the point, and the ten minutes it takes to undo is usually enough for the urge to pass. If you’re blocking someone else who has the admin password, you are not really blocking them.</p><p><strong>A hand-written list ages badly.</strong> Mirror domains and new sites appear constantly. That’s exactly what the filtering resolver is for, and why it’s worth doing both layers rather than picking one.</p><p><strong>If you need more</strong>, the next steps are a separate non-admin account for daily use, a configuration profile for browser policy, and a LaunchDaemon that reapplies the blocklist on a schedule. Worth it only if your situation warrants it — and all three are things you can ask Claude Code to build.</p><p>And the thing worth saying plainly: <strong>if this is compulsive rather than habitual — if it’s costing you sleep, work, or a relationship, and stopping feels genuinely outside your control — a technical block is a support, not a treatment.</strong> It buys you time and space. It doesn’t do the harder work. A therapist who works with compulsive behaviour will do more for you than any script, and there’s nothing weak about going and getting one.</p><h3>The point</h3><p>The reason most people never set any of this up isn’t that they don’t want to. It’s that the free path required knowing about /etc/hosts, IPv6 AAAA records, dscacheutil, and networksetup — and the paid path required handing a stranger a live feed of your screen.</p><p>Claude Code collapses the first path down to a paragraph of plain English. The technical knowledge is still required — it’s just no longer required <em>of you</em>.</p><p>Your machine. Your files. Nothing leaves. No subscription. Fifteen minutes.</p><h3>Appendix: the script</h3><p>Save this as blocksites. It keeps your domain list in /etc/blocked-sites.txt and regenerates a clearly delimited block inside /etc/hosts, so adding and removing sites never involves opening a critical system file by hand.</p><p>(You can skip this entirely and let Claude Code write it — but here it is, because you should be able to see exactly what’s running on your machine.)</p><pre>#!/bin/bash<br>#<br># blocksites — system-wide website blocklist via /etc/hosts<br>#<br># Affects every user account and every app on this Mac.<br># Domain list lives in /etc/blocked-sites.txt (root-owned).<br>#<br># Usage:<br>#   blocksites add &lt;domain&gt; [domain...]   add domains and apply<br>#   blocksites rm  &lt;domain&gt; [domain...]   remove domains and apply<br>#   blocksites list                       show the current blocklist<br>#   blocksites apply                      rebuild /etc/hosts from the list<br>#   blocksites off                        remove the block (list is kept)<br>#   blocksites status                     show whether the block is active</pre><pre>set -euo pipefail</pre><pre>LIST=${BLOCKSITES_LIST:-/etc/blocked-sites.txt}<br>HOSTS=${BLOCKSITES_HOSTS:-/etc/hosts}<br>BEGIN=&#39;# BEGIN blocksites — managed block, edit /etc/blocked-sites.txt instead&#39;<br>END=&#39;# END blocksites&#39;<br>SINK=&#39;0.0.0.0&#39;<br>SINK6=&#39;::&#39;   # without an IPv6 sink, AAAA lookups fall through to real DNS and the block leaks</pre><pre>if [[ $EUID -ne 0 &amp;&amp; -z ${BLOCKSITES_HOSTS:-} ]]; then<br>  echo &quot;blocksites: must run as root (use sudo)&quot; &gt;&amp;2<br>  exit 1<br>fi</pre><pre>secure_list() {<br>  chown root:wheel &quot;$LIST&quot; 2&gt;/dev/null || true<br>  chmod 644 &quot;$LIST&quot; 2&gt;/dev/null || true<br>}</pre><pre>[[ -f $LIST ]] || { : &gt; &quot;$LIST&quot;; secure_list; }</pre><pre># Strip scheme, path, port, leading www. and lowercase — so pasting a full URL works.<br>normalize() {<br>  printf &#39;%s\n&#39; &quot;$1&quot; \<br>    | tr &#39;[:upper:]&#39; &#39;[:lower:]&#39; \<br>    | sed -E &#39;s#^[a-z]+://##; s#/.*$##; s#:[0-9]+$##; s#^www\.##&#39; \<br>    | sed -E &#39;s#^\.+##; s#\.+$##&#39;<br>}</pre><pre>flush_dns() {<br>  dscacheutil -flushcache 2&gt;/dev/null || true<br>  killall -HUP mDNSResponder 2&gt;/dev/null || true<br>}</pre><pre># Rewrite $HOSTS: everything outside the markers is preserved verbatim.<br>apply() {<br>  local tmp<br>  tmp=$(mktemp /tmp/blocksites.XXXXXX)</pre><pre>  awk -v b=&quot;$BEGIN&quot; -v e=&quot;$END&quot; &#39;<br>    $0 == b { skip = 1 }<br>    !skip   { print }<br>    $0 == e { skip = 0 }<br>  &#39; &quot;$HOSTS&quot; &gt; &quot;$tmp&quot;</pre><pre>  # Drop trailing blank lines so repeated applies do not accumulate whitespace.<br>  while [[ -s $tmp &amp;&amp; -z $(tail -n1 &quot;$tmp&quot;) ]]; do<br>    sed -i &#39;&#39; -e &#39;$d&#39; &quot;$tmp&quot;<br>  done</pre><pre>  local count=0<br>  if [[ -s $LIST ]]; then<br>    {<br>      printf &#39;\n%s\n&#39; &quot;$BEGIN&quot;<br>      while IFS= read -r domain; do<br>        [[ -z $domain || $domain == \#* ]] &amp;&amp; continue<br>        printf &#39;%s\t%s\n&#39;     &quot;$SINK&quot;  &quot;$domain&quot;<br>        printf &#39;%s\t%s\n&#39;     &quot;$SINK6&quot; &quot;$domain&quot;<br>        printf &#39;%s\twww.%s\n&#39; &quot;$SINK&quot;  &quot;$domain&quot;<br>        printf &#39;%s\twww.%s\n&#39; &quot;$SINK6&quot; &quot;$domain&quot;<br>        count=$((count + 1))<br>      done &lt; &quot;$LIST&quot;<br>      printf &#39;%s\n&#39; &quot;$END&quot;<br>    } &gt;&gt; &quot;$tmp&quot;<br>    count=$(grep -cvE &#39;^\s*(#|$)&#39; &quot;$LIST&quot; || true)<br>  fi</pre><pre>  # Validate before installing: we must never leave localhost unresolvable.<br>  grep -qE &#39;^\s*127\.0\.0\.1\s+localhost&#39; &quot;$tmp&quot; || {<br>    echo &quot;blocksites: refusing to write — localhost entry missing&quot; &gt;&amp;2<br>    rm -f &quot;$tmp&quot;; exit 1<br>  }</pre><pre>  cat &quot;$tmp&quot; &gt; &quot;$HOSTS&quot;          # preserves existing inode/ownership/mode<br>  rm -f &quot;$tmp&quot;<br>  chown root:wheel &quot;$HOSTS&quot; 2&gt;/dev/null || true<br>  chmod 644 &quot;$HOSTS&quot; 2&gt;/dev/null || true<br>  flush_dns<br>  echo &quot;blocksites: $count domain(s) blocked (plus their www. forms). DNS cache flushed.&quot;<br>}</pre><pre>cmd=${1:-status}<br>shift || true</pre><pre>case &quot;$cmd&quot; in<br>  add)<br>    [[ $# -gt 0 ]] || { echo &quot;usage: blocksites add &lt;domain&gt;...&quot; &gt;&amp;2; exit 1; }<br>    for raw in &quot;$@&quot;; do<br>      d=$(normalize &quot;$raw&quot;)<br>      [[ -n $d ]] || continue<br>      if grep -qxF &quot;$d&quot; &quot;$LIST&quot; 2&gt;/dev/null; then<br>        echo &quot;  already blocked: $d&quot;<br>      else<br>        printf &#39;%s\n&#39; &quot;$d&quot; &gt;&gt; &quot;$LIST&quot;<br>        echo &quot;  + $d&quot;<br>      fi<br>    done<br>    sort -u -o &quot;$LIST&quot; &quot;$LIST&quot;<br>    apply<br>    ;;<br>  rm|remove)<br>    [[ $# -gt 0 ]] || { echo &quot;usage: blocksites rm &lt;domain&gt;...&quot; &gt;&amp;2; exit 1; }<br>    for raw in &quot;$@&quot;; do<br>      d=$(normalize &quot;$raw&quot;)<br>      [[ -n $d ]] || continue<br>      if grep -qxF &quot;$d&quot; &quot;$LIST&quot; 2&gt;/dev/null; then<br>        grep -vxF &quot;$d&quot; &quot;$LIST&quot; &gt; &quot;$LIST.tmp&quot; || true<br>        mv &quot;$LIST.tmp&quot; &quot;$LIST&quot;<br>        secure_list<br>        echo &quot;  - $d&quot;<br>      else<br>        echo &quot;  not in list: $d&quot;<br>      fi<br>    done<br>    apply<br>    ;;<br>  list)<br>    if [[ -s $LIST ]]; then cat &quot;$LIST&quot;; else echo &quot;(blocklist is empty)&quot;; fi<br>    ;;<br>  apply)<br>    apply<br>    ;;<br>  off)<br>    local_tmp=$(mktemp /tmp/blocksites.XXXXXX)<br>    awk -v b=&quot;$BEGIN&quot; -v e=&quot;$END&quot; &#39;<br>      $0 == b { skip = 1 }<br>      !skip   { print }<br>      $0 == e { skip = 0 }<br>    &#39; &quot;$HOSTS&quot; &gt; &quot;$local_tmp&quot;<br>    while [[ -s $local_tmp &amp;&amp; -z $(tail -n1 &quot;$local_tmp&quot;) ]]; do<br>      sed -i &#39;&#39; -e &#39;$d&#39; &quot;$local_tmp&quot;<br>    done<br>    cat &quot;$local_tmp&quot; &gt; &quot;$HOSTS&quot;<br>    rm -f &quot;$local_tmp&quot;<br>    flush_dns<br>    echo &quot;blocksites: block removed from $HOSTS (list preserved in $LIST — &#39;blocksites apply&#39; re-enables).&quot;<br>    ;;<br>  status)<br>    if grep -qF &quot;$BEGIN&quot; &quot;$HOSTS&quot;; then<br>      n=$(grep -cvE &#39;^\s*(#|$)&#39; &quot;$LIST&quot; 2&gt;/dev/null || true)<br>      echo &quot;blocksites: ACTIVE — $n domain(s) blocked&quot;<br>    else<br>      echo &quot;blocksites: inactive&quot;<br>    fi<br>    ;;<br>  *)<br>    sed -n &#39;3,14p&#39; &quot;$0&quot; | sed &#39;s/^# \{0,1\}//&#39;<br>    exit 1<br>    ;;<br>esac</pre><p>Install it:</p><pre>sudo install -o root -g wheel -m 755 blocksites /usr/local/bin/blocksites</pre><p>Everyday use:</p><pre>sudo blocksites add reddit.com redgifs.com   # add and apply<br>sudo blocksites rm reddit.com                # remove and apply<br>sudo blocksites list                         # what&#39;s on the list<br>sudo blocksites status                       # is the block live<br>sudo blocksites off                          # lift it, keep the list<br>sudo blocksites apply                        # put it back</pre><p>A few design decisions worth calling out, because they’re the difference between a script you trust with /etc/hosts and one you don&#39;t:</p><ul><li><strong>Marked region.</strong> Everything outside the BEGIN/END markers is copied through verbatim. Local dev entries, VPN entries — all survive untouched.</li><li><strong>It refuses to write a broken file.</strong> Before installing, it checks the 127.0.0.1 localhost line is still present. A hosts file without it breaks things in confusing ways.</li><li><strong>Input normalization.</strong> Paste a full URL with capitals, a path and a port; it reduces to a bare domain.</li><li><strong>It flushes DNS on every change</strong>, so blocks take effect immediately.</li><li><strong>Idempotent.</strong> Run apply a hundred times and the file is identical each time.</li></ul><h3>Cheat sheet</h3><p>Goal Command or setting Block named sites, all accounts sudo blocksites add example.com Check a block really works dscacheutil -q host -a name example.com See the live block sudo blocksites status Lift it temporarily sudo blocksites off Category filtering, one Mac networksetup -setdnsservers … 1.1.1.3 Category filtering, whole house Router DNS → 1.1.1.3 / 1.0.0.3 Category filtering, one iPhone Screen Time → Limit Adult Websites Named sites, one iPhone Screen Time → Never Allow Flush macOS DNS cache sudo dscacheutil -flushcache</p><p><em>Commands tested on macOS 26.5. Resolver behaviour verified against 1.1.1.3 and 185.228.168.10 at time of writing; filtering services change their categorisation over time, so re-verify with </em><em>dscacheutil rather than assuming.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=642d88df4e63" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[System Design In a Hurry | Part I]]></title>
            <link>https://medium.com/the-modern-scientist/system-design-in-a-hurry-part-i-db703d553ccd?source=rss-8fb24e986396------2</link>
            <guid isPermaLink="false">https://medium.com/p/db703d553ccd</guid>
            <category><![CDATA[system-design-concepts]]></category>
            <dc:creator><![CDATA[Omar Hussein]]></dc:creator>
            <pubDate>Sat, 24 Aug 2024 21:23:06 GMT</pubDate>
            <atom:updated>2024-08-25T17:35:20.706Z</atom:updated>
            <content:encoded><![CDATA[<p>System design is a HUGE space, but my goal here is to get you SUPER functional in a short amount of time, now if you think what I am about to throw at you is a lot. You should see the original content. This part should be very friendly but don’t get used to it.</p><p>Now- Are you Ready ?!!.</p><h3>The Epic Journey of Scaling: From Garage Startup to Tech Giant</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*yvd05UJmyILJJgKYpGJ38g.png" /></figure><p><strong>Picture this:</strong> You’re in a garage, hunched over a laptop, furiously coding the next big thing. Your web app is alive, and your first user (probably your mom) just signed up. Congratulations! But what happens when your brilliant idea catches fire, and suddenly, you’re not juggling one user, but millions?</p><p>Welcome to the wild ride of scaling a web application. Buckle up, future tech mogul, because we’re about to embark on an adventure from zero to millions of users!</p><h3>Chapter 1: The Humble Beginnings</h3><p>Remember that laptop in the garage? That’s your entire tech stack right now. Web server, database, cache — it’s all crammed into one machine. It’s like having your entire office in a studio apartment. Cozy, but not for long.</p><p>Your users (still mostly family and that one weirdly supportive ex) type in your fancy domain name, and voila! They’re connected to your digital brainchild. Simple, right? Oh, sweet summer child, if only it stayed that way.</p><h3>Chapter 2: Growing Pains and Database Dilemmas</h3><p>As word spreads about your app (turns out your ex has a surprisingly large social media following), you realize your trusty laptop is sweating. It’s time for your first big decision: SQL or NoSQL?</p><p><strong>Choosing a database is like choosing a life partner.</strong> SQL databases are the dependable type — great with structure and relationships. NoSQL? That’s your free-spirited option, flexible and ready for anything. Choose wisely; this relationship will last a while.</p><h4>SQL vs. NoSQL: Know Your Options</h4><ul><li><strong>SQL Databases</strong> (Relational Databases): Think of them as well-organized spreadsheets. They’re great for applications where the data is structured, and relationships between data entities are crucial (like financial records or inventory management).</li><li><strong>NoSQL Databases</strong>: Perfect for when your data is unstructured or when you need to handle massive amounts of data across distributed systems (like social media feeds or real-time analytics).</li></ul><h3>Chapter 3: The Great Server Split</h3><p>Remember when you thought you’d be the next Zuckerberg with just one server? Cute. It’s time to separate your web server from your database. Think of it as your app moving out of your parents’ house. It’s a bit scary, but necessary for growth.</p><p>By splitting your server, you’re now able to scale each component independently. Need more power for your web server? Just upgrade or add another one. Is your database getting bogged down with too many read requests? Time to optimize or add more database servers.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VHfYJgLOqCOBiCKM8dlT4Q.png" /></figure><h3>Chapter 4: Scaling Up or Scaling Out? The Eternal Question</h3><p>Now you’re faced with another choice: do you beef up your servers (vertical scaling) or add more of them (horizontal scaling)?</p><ul><li><strong>Vertical Scaling</strong>: Adding more resources (CPU, RAM) to your existing servers. It’s like upgrading your studio apartment to a penthouse but in the same building. This approach works up to a point, but eventually, you’ll hit hardware limitations (and your wallet might feel it too).</li><li><strong>Horizontal Scaling</strong>: Adding more servers to your infrastructure. This is like building more apartments to house your growing population. Each server handles a slice of the workload, allowing you to scale out almost indefinitely. It’s more complex to manage but provides greater flexibility and redundancy.</li></ul><h3>Chapter 5: Load Balancers — The Traffic Cops of the Internet</h3><p>With multiple servers, you need something to direct traffic. Enter load balancers, the unsung heroes of the web. They’re like incredibly efficient traffic cops, making sure no server gets overwhelmed while others twiddle their digital thumbs.</p><p><strong>Why Load Balancers Are Essential:</strong></p><ul><li><strong>Distribute Traffic Evenly</strong>: Ensure no single server is overloaded while others are idle.</li><li><strong>Enhance Reliability</strong>: If a server goes down, the load balancer redirects traffic to healthy servers, maintaining uptime.</li><li><strong>Improve Scalability</strong>: Easily add or remove servers as needed without disrupting the user experience.</li></ul><h3>Chapter 6: Database Drama — The Replication Saga</h3><p>Your database is feeling the strain of fame. Time for some database replication! Think of it as creating a boy band. You have one lead singer (the master database) and several backup dancers (slave databases). The lead handles the hard stuff (writes), while the backups support with the easier tasks (reads).</p><p><strong>Benefits of Database Replication:</strong></p><ul><li><strong>Improved Performance</strong>: Spread read requests across multiple replicas, reducing the load on the master database.</li><li><strong>Increased Reliability</strong>: If the master database fails, promote a replica to take over, ensuring continuity.</li></ul><h3>Chapter 7: The Cache Money Crew</h3><p>By now, your app is cooking! But users are impatient creatures. Enter caching, your new best friend. It’s like having a super-smart assistant who remembers everything, so you don’t have to keep asking the same questions.</p><p>And don’t forget its cool cousin, CDN (Content Delivery Network), who makes sure your static content is always ready for its global audience.</p><p>Now, you might be wondering, “What exactly is a CDN, and why do I need one?”</p><p>Imagine you’re running a global pizza delivery service. Instead of baking all the pizzas in one central kitchen and shipping them worldwide (which would result in a lot of cold, sad pizzas), you set up kitchens all around the world. Each kitchen stores pre-made popular pizzas (your static content) and quickly heats them up when ordered. That’s essentially what a CDN does!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*bG159Wp3h2wgqI_ZWFIKmg.png" /></figure><p>Why You Should Care About CDNs:</p><ul><li>Speed and Performance: CDNs store copies of your static content (images, CSS, JavaScript files) on servers distributed globally. When a user accesses your site, the content is served from the nearest server, reducing load times and improving the user experience.</li><li>Reduced Load on Your Servers: By offloading the delivery of static content to the CDN, your servers are free to handle more important tasks, like processing dynamic requests and running business logic.</li><li>Improved Reliability: CDNs can provide redundancy. If one server goes down, another can take over, ensuring that your site remains available to users around the world.</li><li>Scalability: As your traffic grows, CDNs can handle increased loads without requiring you to add more infrastructure, making them a cost-effective solution for scaling your web application.</li></ul><h3>Chapter 8: Stateless Architecture — The Art of Forgetting</h3><p>Going stateless is like embracing zen philosophy for your servers. No attachments, no memories. Each request is a brand-new day. It’s liberating for your servers and a nightmare for session management. But hey, that’s what JSON Web Tokens (JWTs) are for!</p><p>You might ask, “What’s so special about JWTs, and how do they help in a stateless architecture?”</p><p>JWTs are like digital passports for your users. When a user logs in, your server issues a JWT that contains encoded information about the user, such as their ID and roles. This token is then stored on the client side (usually in local storage or a cookie) and sent with each subsequent request.</p><p>How JWTs Make Stateless Work:</p><ul><li>Self-Contained Tokens: JWTs carry all the information needed to identify a user, meaning the server doesn’t need to store session data. Each server can validate the token and extract user details, making it easy to scale horizontally.</li><li>Security and Efficiency: JWTs are signed to ensure their integrity, preventing tampering. They can also be encrypted to protect sensitive information. This approach reduces the need for database lookups or server-side session storage, speeding up authentication.</li><li>Flexibility: JWTs are versatile and can be used for authentication in both web and mobile applications. They’re also great for scenarios where single sign-on (SSO) is required across multiple services or domains.</li><li>Expiration and Refresh: JWTs can include an expiration time, ensuring that tokens are valid only for a limited period. When a token expires, the user must re-authenticate or use a refresh token to obtain a new JWT, balancing security and user convenience.</li></ul><h3>Chapter 9: Going Global — The Multi-Data Center Tango</h3><p>You’re worldwide, baby! But with great reach comes great responsibility. Multiple data centers ensure your app is fast everywhere. It’s like opening franchise stores, but way cooler and with more blinking lights.</p><p>“Why would I need multiple data centers?” you might wonder.</p><p>Think of data centers like strategically placed warehouses. If you only have one warehouse in New York, it’s going to take a while to deliver a package to someone in Tokyo. But if you have a warehouse in Tokyo too, that delivery is a lot quicker. The same principle applies to data centers:</p><p>Benefits of a Multi-Data Center Strategy:</p><ul><li>Reduced Latency: Users are directed to the nearest data center, minimizing the distance data must travel and speeding up response times.</li><li>Disaster Recovery: If one data center suffers an outage (natural disasters, power failures), your application can automatically redirect traffic to another, minimizing downtime and maintaining availability.</li><li>Load Balancing Across Regions: Distributing traffic across multiple data centers prevents any single location from becoming a bottleneck, ensuring smooth operation even under high loads.</li><li>Regulatory Compliance: For some industries, it’s crucial to store data in specific regions to comply with local laws. Multiple data centers make it easier to adhere to these regulations by localizing data storage.</li></ul><h3>Chapter 10: The Decoupling Dance — Embracing Microservices</h3><p>As your app grows more complex, it’s time to break it down. Microservices architecture is like turning your monolithic app into a bunch of specialized food trucks instead of one big restaurant. Each service does one thing, but does it well.</p><p>“Why go through the hassle of microservices?” you might ask.</p><p>Imagine your monolithic app as a giant Swiss Army knife. It’s got everything, but it’s cumbersome and hard to update. Microservices are like having separate, specialized tools that you can easily upgrade or replace without affecting the others.</p><p>Advantages of Microservices:</p><ul><li>Independent Development and Deployment: Each service can be developed, deployed, and scaled independently, allowing your teams to work faster and more efficiently.</li><li>Resilience and Fault Isolation: If one service fails, it doesn’t necessarily bring down the entire application. This isolation improves overall reliability and uptime.</li><li>Technology Flexibility: Different services can be written in different languages or use different databases, giving you the flexibility to choose the best tools for each job.</li><li>Better Resource Utilization: Scale each microservice according to its specific needs. For example, your authentication service might require less computing power than your image processing service, allowing you to optimize costs and resources.</li></ul><h3>Chapter 11: Automate All the Things!</h3><p>Finally, as your empire grows, you can’t possibly keep an eye on everything. Automation and monitoring tools become your new best friends. They’re like having a really attentive robot butler who tells you when things are going wrong before you even notice.</p><p>But wait, what should you automate, and why?</p><p>The Essentials of Automation and Monitoring:</p><ul><li>Continuous Integration/Continuous Deployment (CI/CD): Automate the process of integrating code changes and deploying them to production. This reduces human error, speeds up development cycles, and ensures consistent quality.</li><li>Infrastructure as Code (IaC): Use tools like Terraform or AWS CloudFormation to automate the provisioning and management of your infrastructure. This makes scaling up or down as easy as running a script.</li><li>Automated Testing: Implement automated tests to catch bugs and issues before they reach production, ensuring a smoother user experience.</li><li>Monitoring and Alerting: Set up monitoring tools (like Prometheus or Datadog) to keep track of application performance, server health, and user metrics. Configure alerts to notify you of potential problems, allowing you to proactively address issues before they escalate.</li></ul><h3>The Never-Ending Story</h3><p>And there you have it! You’ve scaled from a single user to millions. But remember, in the world of tech, there’s no “happily ever after.” It’s more like “happily keep iterating forever after.”</p><p>Your journey of scaling is never truly over. There’s always a new challenge, a new technology, a new paradigm. But isn’t that why we got into this crazy world of web development in the first place?</p><p>So, future tech titan, are you ready to start your scaling adventure? Remember, every tech giant started with a single server and a dream. Your empire awaits!</p><h3>Frequently Anticipated Questions</h3><p>As you embark on your scaling journey, you might find yourself pondering these questions:</p><ol><li>Q: Do I need to implement all these scaling strategies at once? A: Not at all! Scaling is a gradual process. Start with the basics like separating your web and data tiers, then implement other strategies as your user base grows and you identify specific needs.</li><li>Q: How do I know when it’s time to scale? A: Keep an eye on key metrics like response times, server load, and user complaints. If you notice slowdowns or your current setup struggling to handle traffic, it’s probably time to consider scaling.</li><li>Q: Is horizontal scaling always better than vertical scaling? A: Not necessarily. For smaller applications, vertical scaling (upgrading your existing servers) can be simpler and more cost-effective. However, as you grow, horizontal scaling often provides more flexibility and fault tolerance.</li><li>Q: How do microservices compare to a monolithic architecture in terms of development speed? A: Initially, a monolithic architecture can be faster to develop. However, as your application grows, microservices can speed up development by allowing teams to work independently on different services.</li><li>Q: Are there any downsides to using a CDN? A: While CDNs offer many benefits, they can add complexity to your setup and may have associated costs. Also, if your content changes frequently, you need to ensure your CDN is updated promptly to avoid serving stale content.</li><li>Q: How do I handle data consistency across multiple data centers? A: This is a complex issue known as the CAP theorem problem. Common strategies include eventual consistency models, conflict resolution algorithms, and distributed consensus protocols like Paxos or Raft.</li><li>Q: Is it possible to over-optimize or over-scale my application? A: Yes! It’s important to scale based on actual needs rather than anticipated ones. Premature optimization can lead to unnecessary complexity and costs. Always measure and analyze before scaling.</li><li>Q: How do I ensure security when scaling, especially with microservices? A: Security in a scaled environment involves many aspects: use HTTPS everywhere, implement proper authentication and authorization (like OAuth 2.0), use security groups and firewalls, and regularly audit your systems. For microservices, consider using an API gateway to handle security concerns centrally.</li></ol><p>Remember, scaling is as much an art as it is a science. These questions are just the beginning of your journey. Keep learning, stay curious, and don’t be afraid to experiment!</p><h3>Rules of Thumb for Back-of-the-Envelope Estimation in System Design</h3><p>When you’re faced with a system design problem or need to estimate capacity for a new project, follow these rules of thumb to guide your back-of-the-envelope calculations:</p><h3>1. Start with the Scale</h3><p>Rule: Always begin by estimating the scale of your system.</p><p>How to do it:</p><ul><li>Determine your total user base (e.g., monthly active users)</li><li>Estimate daily active users (usually 10–25% of total users)</li><li>Calculate requests per second:</li><li>Start with daily operations</li><li>Divide by seconds in a day (86,400)</li><li>Add a peak factor (usually 2–3x average)</li></ul><p>Example:</p><ul><li>1 million total users</li><li>200,000 daily active users (20%)</li><li>2 million daily operations</li><li>Average QPS = 2,000,000 / 86,400 ≈ 23</li><li>Peak QPS = 23 * 3 = 69</li></ul><h3>2. Estimate Storage</h3><p>Rule: Calculate storage needs based on data creation and retention policies.</p><p>How to do it:</p><ul><li>Determine the size of each data item</li><li>Calculate daily data generation</li><li>Multiply by your data retention period</li></ul><p>Example:</p><ul><li>Each user profile is 200 KB</li><li>1,000 new users per day</li><li>Data retained for 5 years</li><li>Daily storage increase = 1,000 * 200 KB = 200 MB</li><li>5-year storage = 200 MB * 365 * 5 ≈ 365 GB</li></ul><h3>3. Assess Bandwidth</h3><p>Rule: Estimate both incoming and outgoing bandwidth.</p><p>How to do it:</p><ul><li>Calculate the size of each request and response</li><li>Multiply by your peak QPS</li></ul><p>Example:</p><ul><li>Average request/response size is 100 KB</li><li>Peak QPS is 69</li><li>Bandwidth = 100 KB * 69 = 6.9 MB/s ≈ 55 Mbps</li></ul><h3>4. Compute CPU Needs</h3><p>Rule: Estimate based on CPU-intensive operations per request.</p><p>How to do it:</p><ul><li>Determine CPU-intensive operations per request</li><li>Estimate CPU cores needed per operation</li><li>Multiply by peak QPS</li></ul><p>Example:</p><ul><li>Each request requires 0.1 CPU cores for 100ms</li><li>Peak QPS is 69</li><li>CPU cores needed = 0.1 * 69 * 0.1 ≈ 0.69 cores</li></ul><h3>5. Calculate Memory Requirements</h3><p>Rule: Estimate based on active dataset and caching needs.</p><p>How to do it:</p><ul><li>Determine the size of your active dataset</li><li>Add overhead for OS and applications (usually 20–30%)</li><li>Consider caching requirements (often 20–30% of active data)</li></ul><p>Example:</p><ul><li>Active dataset is 500 GB</li><li>OS and application overhead: 100 GB</li><li>Cache: 100 GB</li><li>Total memory needed: 700 GB</li></ul><h3>6. Plan for Redundancy and Growth</h3><p>Rule: Always add capacity for redundancy and future growth.</p><p>How to do it:</p><ul><li>Multiply your estimates by 2–3 for redundancy</li><li>Plan for 2–3 years of growth (often 20–50% year-over-year)</li></ul><p>Example:</p><ul><li>If you need 10 servers based on current estimates:</li><li>20–30 servers for redundancy</li><li>30–45 servers accounting for 2 years of 20% annual growth</li></ul><h3>7. Consider Availability Requirements</h3><p>Rule: Higher availability requires more resources and complexity.</p><p>How to do it:</p><ul><li>Determine required uptime (e.g., 99.99% vs 99.999%)</li><li>Plan for multi-region deployment for higher availability</li><li>Implement robust monitoring and automated failover</li></ul><p>Example:</p><ul><li>For 99.999% availability:</li><li>Deploy in at least 3 regions</li><li>Implement real-time data replication</li><li>Set up automated failover mechanisms</li></ul><h3>8. Use Powers of 2 and Round Numbers</h3><p>Rule: Simplify calculations using powers of 2 and round numbers.</p><p>How to do it:</p><ul><li>Use 2¹⁰ (1024) as thousand, 2²⁰ as million, etc.</li><li>Round to the nearest power of 10 or 2</li></ul><p>Example:</p><ul><li>1.7 million users becomes 2 million</li><li>240 MB becomes 256 MB (2⁸)</li></ul><h3>9. State Your Assumptions Clearly</h3><p>Rule: Always clearly state the assumptions behind your estimates.</p><p>How to do it:</p><ul><li>Write down each assumption as you make it</li><li>Be prepared to adjust calculations if assumptions change</li></ul><p>Example: “Assuming 20% of total users are active daily, and each user performs 10 operations per day…”</p><h3>10. Sanity Check Your Results</h3><p>Rule: Always perform a sanity check on your final numbers.</p><p>How to do it:</p><ul><li>Compare your results to similar known systems</li><li>Verify that the proportions make sense (e.g., storage vs. bandwidth)</li><li>If something seems off, revisit your assumptions and calculations</li></ul><p>Example: If your estimation suggests you need more servers than Google, you’ve probably made a mistake somewhere!</p><p>Remember, these rules of thumb are starting points. Always be ready to adjust based on the specific requirements of your system and the realities of your business. The goal of back-of-the-envelope estimation is to get a reasonable approximation quickly, not to produce exact figures.</p><p>— —</p><p>Now, this last part which is the rules of thumbs this is as much for me as it is for you because frankly that was a lot so here it is for me and you to look up.</p><h3>Rules of Thumb for Back-of-the-Envelope Estimation in System Design</h3><p>When you’re faced with a system design problem or need to estimate capacity for a new project, follow these rules of thumb to guide your back-of-the-envelope calculations:</p><h3>1. Start with the Scale</h3><p>Rule: Always begin by estimating the scale of your system.</p><p>How to do it:</p><ul><li>Determine your total user base (e.g., monthly active users)</li><li>Estimate daily active users (usually 10–25% of total users)</li><li>Calculate requests per second:</li><li>Start with daily operations</li><li>Divide by seconds in a day (86,400)</li><li>Add a peak factor (usually 2–3x average)</li></ul><p>Example:</p><ul><li>1 million total users</li><li>200,000 daily active users (20%)</li><li>2 million daily operations</li><li>Average QPS = 2,000,000 / 86,400 ≈ 23</li><li>Peak QPS = 23 * 3 = 69</li></ul><h3>2. Estimate Storage</h3><p>Rule: Calculate storage needs based on data creation and retention policies.</p><p>How to do it:</p><ul><li>Determine the size of each data item</li><li>Calculate daily data generation</li><li>Multiply by your data retention period</li></ul><p>Example:</p><ul><li>Each user profile is 200 KB</li><li>1,000 new users per day</li><li>Data retained for 5 years</li><li>Daily storage increase = 1,000 * 200 KB = 200 MB</li><li>5-year storage = 200 MB * 365 * 5 ≈ 365 GB</li></ul><h3>3. Assess Bandwidth</h3><p>Rule: Estimate both incoming and outgoing bandwidth.</p><p>How to do it:</p><ul><li>Calculate the size of each request and response</li><li>Multiply by your peak QPS</li></ul><p>Example:</p><ul><li>Average request/response size is 100 KB</li><li>Peak QPS is 69</li><li>Bandwidth = 100 KB * 69 = 6.9 MB/s ≈ 55 Mbps</li></ul><h3>4. Compute CPU Needs</h3><p>Rule: Estimate based on CPU-intensive operations per request.</p><p>How to do it:</p><ul><li>Determine CPU-intensive operations per request</li><li>Estimate CPU cores needed per operation</li><li>Multiply by peak QPS</li></ul><p>Example:</p><ul><li>Each request requires 0.1 CPU cores for 100ms</li><li>Peak QPS is 69</li><li>CPU cores needed = 0.1 * 69 * 0.1 ≈ 0.69 cores</li></ul><h3>5. Calculate Memory Requirements</h3><p>Rule: Estimate based on active dataset and caching needs.</p><p>How to do it:</p><ul><li>Determine the size of your active dataset</li><li>Add overhead for OS and applications (usually 20–30%)</li><li>Consider caching requirements (often 20–30% of active data)</li></ul><p>Example:</p><ul><li>Active dataset is 500 GB</li><li>OS and application overhead: 100 GB</li><li>Cache: 100 GB</li><li>Total memory needed: 700 GB</li></ul><h3>6. Plan for Redundancy and Growth</h3><p>Rule: Always add capacity for redundancy and future growth.</p><p>How to do it:</p><ul><li>Multiply your estimates by 2–3 for redundancy</li><li>Plan for 2–3 years of growth (often 20–50% year-over-year)</li></ul><p>Example:</p><ul><li>If you need 10 servers based on current estimates:</li><li>20–30 servers for redundancy</li><li>30–45 servers accounting for 2 years of 20% annual growth</li></ul><h3>7. Consider Availability Requirements</h3><p>Rule: Higher availability requires more resources and complexity.</p><p>How to do it:</p><ul><li>Determine required uptime (e.g., 99.99% vs 99.999%)</li><li>Plan for multi-region deployment for higher availability</li><li>Implement robust monitoring and automated failover</li></ul><p>Example:</p><ul><li>For 99.999% availability:</li><li>Deploy in at least 3 regions</li><li>Implement real-time data replication</li><li>Set up automated failover mechanisms</li></ul><h3>8. Use Powers of 2 and Round Numbers</h3><p>Rule: Simplify calculations using powers of 2 and round numbers.</p><p>How to do it:</p><ul><li>Use 2¹⁰ (1024) as thousand, 2²⁰ as million, etc.</li><li>Round to the nearest power of 10 or 2</li></ul><p>Example:</p><ul><li>1.7 million users becomes 2 million</li><li>240 MB becomes 256 MB (2⁸)</li></ul><h3>9. State Your Assumptions Clearly</h3><p>Rule: Always clearly state the assumptions behind your estimates.</p><p>How to do it:</p><ul><li>Write down each assumption as you make it</li><li>Be prepared to adjust calculations if assumptions change</li></ul><p>Example: “Assuming 20% of total users are active daily, and each user performs 10 operations per day…”</p><h3>10. Sanity Check Your Results</h3><p>Rule: Always perform a sanity check on your final numbers.</p><p>How to do it:</p><ul><li>Compare your results to similar known systems</li><li>Verify that the proportions make sense (e.g., storage vs. bandwidth)</li><li>If something seems off, revisit your assumptions and calculations</li></ul><p>Example: If your estimation suggests you need more servers than Google, you’ve probably made a mistake somewhere!</p><p>Remember, these rules of thumb are starting points. Always be ready to adjust based on the specific requirements of your system and the realities of your business. The goal of back-of-the-envelope estimation is to get a reasonable approximation quickly, not to produce exact figures.</p><p>The rest of the parts are coming soon !</p><p>NEXT : The details of the framework !</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=db703d553ccd" width="1" height="1" alt=""><hr><p><a href="https://medium.com/the-modern-scientist/system-design-in-a-hurry-part-i-db703d553ccd">System Design In a Hurry | Part I</a> was originally published in <a href="https://medium.com/the-modern-scientist">The Modern Scientist</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building a Simple Yet Engaging Browser Game Extension: The ‘1,2’ Game]]></title>
            <link>https://medium.com/the-modern-scientist/building-a-simple-yet-engaging-browser-game-extension-the-1-2-game-834d1c24106a?source=rss-8fb24e986396------2</link>
            <guid isPermaLink="false">https://medium.com/p/834d1c24106a</guid>
            <category><![CDATA[webdev]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[browser-extension]]></category>
            <category><![CDATA[brain-teaser]]></category>
            <dc:creator><![CDATA[Omar Hussein]]></dc:creator>
            <pubDate>Wed, 31 Jul 2024 03:41:36 GMT</pubDate>
            <atom:updated>2024-07-31T15:46:00.317Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0_-VhRdP1uM7iRFTF5DqRg.png" /></figure><h4>Introduction</h4><p>In the world of web development, creating a browser extension can be an exciting way to enhance your skills and create something fun and interactive. Today, I’ll walk you through the process of building a simple yet engaging number game called “1,2” as a browser extension. This project is perfect for beginners looking to dip their toes into extension development or for more experienced developers wanting a quick, enjoyable coding exercise who have not built a browser extension before.</p><p>What is the “1,2” Game? The “1,2” game is a two-player number game where players take turns adding either 1 or 2 to a running sum, aiming to reach a predetermined target number. The player who reaches the target number exactly wins the game. It’s simple, yet it involves strategy and can be surprisingly addictive!. Also- I am unbeatable in it.</p><p><em>Keep in mind that browser extension can be very useful in most areas even in AI.</em></p><p>Here’s an example of how the game would work between two players (You and the Opponent) with the target number being 10.</p><h3>Game Example with Target 10</h3><h4>Initial Setup</h4><ul><li>Target Number: 10</li><li>Players: “You” and “Opponent”</li><li>The starting player is determined randomly.</li></ul><h4>Game Flow</h4><p><strong>Coin Toss</strong></p><ul><li>Let’s assume the coin toss determines that “You” go first.</li></ul><p><strong>Turn 1 (You)</strong></p><ul><li><strong>Choices</strong>: 1 or 1,2</li><li><strong>Action</strong>: You choose 1,2</li><li><strong>Current Sum</strong>: 2</li><li><strong>Commentary</strong>: “You chose 1,2. The sum increased from 0 to 2. It’s now Opponent’s turn.”</li></ul><p><strong>Turn 2 (Opponent)</strong></p><ul><li><strong>Choices</strong>: 3 or 3,4</li><li><strong>Action</strong>: Opponent chooses 3,4</li><li><strong>Current Sum</strong>: 4</li><li><strong>Commentary</strong>: “Opponent chose 3,4. The sum increased from 2 to 4. It’s now Your turn.”</li></ul><p><strong>Turn 3 (You)</strong></p><ul><li><strong>Choices</strong>: 5 or 5,6</li><li><strong>Action</strong>: You choose 5</li><li><strong>Current Sum</strong>: 5</li><li><strong>Commentary</strong>: “You chose 5. The sum increased from 4 to 5. It’s now Opponent’s turn.”</li></ul><p><strong>Turn 4 (Opponent)</strong></p><ul><li><strong>Choices</strong>: 6 or 6,7</li><li><strong>Action</strong>: Opponent chooses 6,7</li><li><strong>Current Sum</strong>: 7</li><li><strong>Commentary</strong>: “Opponent chose 6,7. The sum increased from 5 to 7. It’s now Your turn.”</li></ul><p><strong>Turn 5 (You)</strong></p><ul><li><strong>Choices</strong>: 8 or 8,9</li><li><strong>Action</strong>: You choose 8,9</li><li><strong>Current Sum</strong>: 9</li><li><strong>Commentary</strong>: “You chose 8,9. The sum increased from 7 to 9. It’s now Opponent’s turn.”</li></ul><p><strong>Turn 6 (Opponent)</strong></p><ul><li><strong>Choices</strong>: 10</li><li><strong>Action</strong>: Opponent chooses 10</li><li><strong>Current Sum</strong>: 10</li><li><strong>Commentary</strong>: “Opponent chose 10. The sum increased from 9 to 10. Opponent wins!”</li></ul><h3>Game Over</h3><ul><li><strong>Winner</strong>: Opponent</li></ul><h4>Notes</h4><ul><li>The players take turns choosing either one or two numbers.</li><li>The choices are always consecutive numbers starting from the current sum + 1.</li><li>The game continues until one player reaches the exact target number.</li><li>The player who reaches the exact target number wins the game.</li></ul><h4>Setting Up the Project</h4><p>To begin, we’ll need to set up our project structure. Create a new directory for your extension and include the following files:</p><ol><li>manifest.json (for extension configuration)</li><li>popup.html (for the game interface)</li><li>popup.js (for game logic)</li><li>styles.css (for styling)</li></ol><p>The Game Logic: The heart of our game lies in the Game class within popup.js. Let&#39;s break down some key components:</p><pre>class Game {<br>  constructor(targetNumber, opponent) {<br>    this.targetNumber = targetNumber;<br>    this.opponent = opponent;<br>    this.currentSum = 0;<br>    this.currentPlayer = Math.random() &lt; 0.5 ? &#39;You&#39; : &#39;Opponent&#39;;<br>    this.winner = null;<br>  }<br><br>  // ... other methods ...<br>}</pre><p>This constructor initializes our game state, randomly choosing the starting player.</p><p>The play method handles each move:</p><pre>play(choice) {<br>  const [lower, upper] = choice;<br>  this.currentSum = upper;<br><br>  if (this.currentSum === this.targetNumber) {<br>    this.winner = this.currentPlayer;<br>  } else {<br>    this.currentPlayer = this.currentPlayer === &#39;You&#39; ? &#39;Opponent&#39; : &#39;You&#39;;<br>  }<br><br>  return true;<br>}</pre><p>The easy opponent chooses randomly, while the impossible opponent uses a strategy to play optimally.</p><p>User Interface and Interaction: The updateUI function refreshes the game display after each move:</p><pre>function updateUI() {<br>  // ... update display elements ...<br><br>  if (game.currentPlayer === &#39;You&#39; &amp;&amp; !game.winner) {<br>    const choices = game.getChoices();<br>    choices.forEach(choice =&gt; {<br>      const button = document.createElement(&#39;button&#39;);<br>      button.textContent = choice[0] === choice[1] ? choice[0] : `${choice[0]},${choice[1]}`;<br>      button.addEventListener(&#39;click&#39;, () =&gt; playMove(choice));<br>      choicesElement.appendChild(button);<br>    });<br>  } else if (game.currentPlayer === &#39;Opponent&#39; &amp;&amp; !game.winner) {<br>    choicesElement.textContent = &#39;Opponent is thinking...&#39;;<br>  }<br><br>  // ... update result display ...<br>}</pre><p>This function dynamically creates buttons for the player’s choices or displays a waiting message during the opponent’s turn.</p><p>Styling and Animation: To make the game more visually appealing, we’ve added CSS animations for various game events. For example, the win animation:</p><pre>function showWinAnimation() {<br>  const winAnimation = document.getElementById(&#39;win-animation&#39;);<br>  winAnimation.style.display = &#39;block&#39;;<br>  setTimeout(() =&gt; {<br>    winAnimation.style.display = &#39;none&#39;;<br>  }, 3000);<br>}</pre><p>This function displays a winning animation for 3 seconds when a player wins.</p><p>The whole code is here; try it : <a href="https://github.com/OMS1996/12browsergame/blob/main/12/popup.js">https://github.com/OMS1996/12browsergame/blob/main/12/popup.js</a></p><p>But you are here not to see how the method works, are you ? <br>You are here for two things : The browser extension and to play the game.</p><p>SO,</p><h4>Browser Extension Development: From Code to Chrome</h4><p>After developing our “1,2” game, the next step is turning it into a browser extension. This process involves a few key components and steps specific to extension development. As we go make sure to have the repo I shared with you on the ready.</p><p>Let’s dive into the details:</p><p><strong>The Manifest File</strong></p><p>At the heart of every Chrome extension is the manifest.json file. This file is crucial as it provides important information about your extension to the browser. Here&#39;s a basic manifest.json for our game:</p><pre>{<br>  &quot;manifest_version&quot;: 2,<br>  &quot;name&quot;: &quot;1,2 Number Game&quot;,<br>  &quot;version&quot;: &quot;1.0&quot;,<br>  &quot;description&quot;: &quot;A simple number game extension&quot;,<br>  &quot;browser_action&quot;: {<br>    &quot;default_popup&quot;: &quot;popup.html&quot;<br>  },<br>  &quot;permissions&quot;: [&quot;storage&quot;]<br>}</pre><p>Key components:</p><ul><li>manifest_version: Specifies which version of the Chrome manifest file format we&#39;re using.</li><li>name and description: Metadata about your extension.</li><li>browser_action: Defines what happens when the user clicks the extension icon. In our case, it opens popup.html.</li><li>permissions: Lists any special permissions your extension needs.</li></ul><p><strong>Directory Structure</strong></p><pre>my-extension/<br>  ├── manifest.json<br>  ├── popup.html<br>  ├── popup.js<br>  └── styles.css</pre><p><strong>Loading the Extension in Chrome</strong></p><p><em>To test your extension in Chrome:</em></p><p>a. Open Chrome and navigate to chrome://extensions/. b. Enable &quot;Developer mode&quot; by toggling the switch in the top right corner. c. Click &quot;Load unpacked&quot; and select your extension directory.</p><p>Your extension should now appear in the list and be available in your browser toolbar.</p><p><strong>Debugging</strong></p><p><em>Chrome provides powerful tools for debugging extensions:</em></p><ul><li>Click “Inspect views: popup.html” on your extension’s card in chrome://extensions/ to open DevTools for your popup.</li><li>Use console.log() statements in your JavaScript to output debug information.</li><li>The “Errors” button on the extension’s card will show any runtime errors.</li></ul><p><strong>Browser Storage</strong></p><p>If you want to save game states or settings, you can use Chrome’s storage API. Add this permission to your manifest.json:</p><pre>&quot;permissions&quot;: [&quot;storage&quot;]</pre><p>Then in your JavaScript</p><pre>chrome.storage.sync.set({key: value}, function() {<br>  console.log(&#39;Value is set to &#39; + value);<br>});<br><br>chrome.storage.sync.get([&#39;key&#39;], function(result) {<br>  console.log(&#39;Value currently is &#39; + result.key);<br>});</pre><p><strong>Publishing Your Extension</strong></p><p>Once your extension is ready:</p><p>a. Create a .zip file of your extension directory.</p><p>b. Go to the Chrome Developer Dashboard (you&#39;ll need a one-time developer account fee Something around 5$).</p><p>c. Click &quot;New Item&quot; and upload your .zip file. d. Fill in all required fields and submit for review.</p><p>Updating Your Extension</p><p>To update your published extension:</p><p>a. Increment the “version” in your manifest.json.</p><p>b. Create a new .zip file.</p><p>c. In the Developer Dashboard, click &quot;Edit&quot; on your extension and upload the new .zip.</p><p>Here is a video on the matter :<br><a href="https://www.youtube.com/watch?v=kKBc2Q5JO9Y&amp;t=8s">https://www.youtube.com/watch?v=kKBc2Q5JO9Y&amp;t=8s</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=834d1c24106a" width="1" height="1" alt=""><hr><p><a href="https://medium.com/the-modern-scientist/building-a-simple-yet-engaging-browser-game-extension-the-1-2-game-834d1c24106a">Building a Simple Yet Engaging Browser Game Extension: The ‘1,2’ Game</a> was originally published in <a href="https://medium.com/the-modern-scientist">The Modern Scientist</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Networking in a Hurry ]]></title>
            <link>https://medium.com/the-modern-scientist/networking-in-a-hurry-51455db8b72a?source=rss-8fb24e986396------2</link>
            <guid isPermaLink="false">https://medium.com/p/51455db8b72a</guid>
            <category><![CDATA[networking-interview]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[networking]]></category>
            <category><![CDATA[computer-science]]></category>
            <category><![CDATA[networking-course]]></category>
            <dc:creator><![CDATA[Omar Hussein]]></dc:creator>
            <pubDate>Tue, 16 Jan 2024 17:13:38 GMT</pubDate>
            <atom:updated>2024-01-30T19:03:59.704Z</atom:updated>
            <content:encoded><![CDATA[<p>You don’t have to be a DevOps Engineer or even a Networking Engineer to benefit from networking. as a Machine Learning Engineer with limited experience in Networking, I’ve relied on fundamental concepts to navigate this domain. However, gaining a comprehensive understanding of the intricacies, purposes, and mechanisms behind networking has proven invaluable. It not only aids in immediate problem-solving but also enhances my ability to swiftly acquire new knowledge on the job. So the knowledge here is designed to give you a solid understanding for most use cases but by no means is it all of Networking- Just the parts you need to understand.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*uFtYgHUdghyaXVJT" /></figure><p>We’ll begin with a relatable story to set the stage, followed by a detailed exploration of its components but if you are not ready for the <strong>story</strong> go through the <strong>basics</strong> section. Remember to be patient and that it is okay to not understand all the lingo at one go, Ready to dive in? Let’s get started, considering your time is valuable.</p><blockquote>DISCLAIMER : In this post, my aim is to provide you with a clear and concise breakdown of networking concepts in a practical and that is why I explain TCP/IP as it reflects how real computer networks are implemented making it much more relevant to learn despite OSI’s theoretical vision. B</blockquote><blockquote>The OSI model is a theoretical framework that describes a complete range of functions required for network communications.</blockquote><h3>Basics</h3><p>The <strong><em>client-server model</em></strong> is all about requests and responses between two components — a client and a server.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*7dEVTTtYJwcsACZ3" /></figure><p>A client is an application or computer that makes requests to access resources or services. This could be a web browser, mobile app, etc.</p><p>A server is a computer that receives these requests and provides the requested resource or performs a task to fulfill the client’s request. Servers are usually more powerful computers.</p><p>For example, when you type a website URL in your web browser (client):</p><ol><li>The browser sends an HTTP request message to the web server asking it to send the website page.</li><li>The server receives the request, processes it, and finds or generates the page data.</li><li>The server then sends an HTTP response back to the browser with the requested website page content (usually HTML, CSS, JS files).</li><li>The browser displays the pages, images, etc. on your screen.</li></ol><h4>IP</h4><p>Here is a simplified explanation of IP addresses:</p><p>IP addresses are like home addresses for devices on the internet. They uniquely identify each device so that data can be sent to the correct destination.</p><p>When you type a website name like <a href="https://etherealai.io/">etherealai.io</a> or <a href="https://www.apple.com/">apple.com</a> into your browser, a DNS service looks up the website’s IP address. This allows your device to make a request to that address to load the site.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2Am-JuABqc-6vzu-X1ufpA.png" /></figure><p>both apple &amp; etherealai servers can then send back the website data to your device’s IP address in response. This allows two-way communication over the internet between your device and the website’s servers.</p><p>So IP addresses act like postal addresses that ensure internet data reaches the intended recipients. DNS services help map domain names we can remember to numerical IP addresses that devices use to route traffic. Together, they allow seamless data transfer to the right destinations.</p><h4>Requests</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/499/1*LOXeu8TAPfHJjXq6hvsv5g.png" /></figure><p>A request is like asking someone a question. It is how a client program initiates communication and demands resources from a server.</p><p>For example, when you type youtube.com in the browser (client program) to visit youtube website:</p><ol><li>The browser constructs an HTTP <strong><em>GET</em></strong> request message for youtube web server, asking — “please give me the homepage for youtube.com”.</li><li>This request message also contains the address of the browser, so youtube knows where to send back the response.</li><li>The youtube web server receives this request.</li></ol><p>So in essence, a web request is a message from a client asking a web server to provide some resource like a webpage, image, file or data. This initiation of communication by the client is termed a request.</p><p><strong>Here is a simple explanation of some common HTTP requests:</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*93j08HoRx5XE6Xfa" /></figure><p><strong>GET</strong> — Fetches data from a server. For example, getting a web page, image or other file from a website:</p><p>“Hey server, can you GET me the homepage file?”</p><p><strong>POST</strong> — Sends user-generated data to the server. For example, submitting a form on a website:</p><p>“Hey server, here’s some form data I want you to POST to your database.”</p><p><strong>PUT</strong> — Uploads data to update something on the server. For example, uploading a file or updating a database entry:</p><p>“Hey server, please PUT this new file in place of the old one.”</p><p><strong>DELETE</strong> — Deletes data from the server. For example, deleting a file:</p><p>“Hey server, please DELETE that document you have stored.”</p><p><strong>So in short:</strong></p><p><strong>GET fetches data POST submits user data PUT updates data DELETE erases data</strong></p><p>These requests allow the client and server to perform basic CRUD (create, read, update, delete) operations on data over the web.</p><h4>HTTP</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*Xq_CEqNkHT65pN0Y.jpg" /></figure><p>HTTP (Hypertext Transfer Protocol) is the communication rules between the client and the server. When Alice types a website URL into her browser (the client):</p><ul><li>The browser uses HTTP to send a GET request message to the server asking for the website content</li><li>The server understands this HTTP request, fetches the website content, and constructs an HTTP response with the content</li><li>The server then transmits the HTTP response back to Alice’s browser with the website files</li><li>Alice’s browser receives this response and displays the website to Alice using the HTML, image files etc. included in it.</li></ul><p><strong>Now repeat this 7 times- I mean it. do it.</strong></p><blockquote>HTTP/HTTPs is in the application layer</blockquote><blockquote>TCP is in the Transport layer</blockquote><h4><strong><em>End of Basics</em></strong></h4><h3>Preface</h3><p><em>TCP/IP Model Overview</em></p><p>Before we dive into the story of Alice and Bob, let’s look at the layers of the TCP/IP model that will be referenced, <em>do not worry too much about all of them</em> we will be mainly focusing on some of them. The important thing is that we are using now <strong>HTTP</strong> from the application layer, <strong>TCP</strong> from the transport layer and <strong>IP</strong> in the internet layer.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/453/1*VDpRjj_EsDa19MWRtPPqaQ.png" /></figure><p>Each layer of the TCP/IP model has specific protocols and types of data units it handles, which play a crucial role in how data is transmitted across a network. With this model in mind, we will follow the journey of data as it travels from Alice to Bob, encapsulated and transformed at each step of the process. We will be walking through this top-down. From Application layer (<strong>HTTP</strong>) through Transport layer (<strong>where are a lot of the action happens starting with TCP</strong>) and so on.</p><h3>The Story of Alice and Bob in the World of Networking</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/941/1*HSCO61NgDh804pyk-4kqzA.png" /></figure><p>Client-Server Model: A model of interaction in a network where a client (Alice) requests resources or services, and a server (Bob) provides them.</p><h4>Beginning the Journey: Alice’s Request</h4><ol><li>Alice’s Intent: Alice wants to access a webpage hosted on Bob’s server.</li><li>Private IP and MAC Address: Alice’s device has a private IP address (e.g., 192.168.1.101) in her LAN and a unique MAC address (e.g., A1:B2:C3:D4:E5:F6).</li><li>DNS Query: Alice’s device checks her DNS cache for Bob’s server IP. If it’s not there, a DNS query is sent to resolve Bob’s domain name to a public IP address.</li><li>DNS Cache Update: Once resolved, the DNS information is stored in Alice’s cache for future use.</li></ol><h4>Networking Layers in Action</h4><ol><li>Application Layer (HTTP Request): Alice’s browser creates an HTTP GET request.</li><li>Transport Layer (TCP Segmentation): This request is segmented by TCP. Each segment is labelled with sequence numbers for ordered reassembly and includes control information for reliable delivery.</li><li>Network Layer (IP Datagram): Each TCP segment is then encapsulated in an IP datagram. This datagram includes Alice’s private IP as the source and Bob’s public IP as the destination.</li><li>Data Link Layer (Ethernet Frame): The IP datagram is further encapsulated into an Ethernet frame, which includes Alice’s MAC address and her router’s MAC address.</li></ol><h4>On the Road: NAT and Routing</h4><ol><li>NAT at Alice’s Router: Alice’s router uses Network Address Translation (NAT) to replace Alice’s private IP with its own public IP in the datagrams.</li><li>Routing Through the Internet: These datagrams traverse the internet, hopping through routers using protocols like BGP. Here, stateless decisions are made solely based on destination IP, ignoring what the packet contains (payload).</li></ol><h4>Entering Bob’s Domain: VPC and Security</h4><ol><li>Bob’s VPC: Bob’s server is in an Amazon VPC, an isolated network within AWS’s cloud infrastructure. 2. Security Groups: The VPC uses security groups, acting as stateful firewalls, to manage incoming traffic. These groups allow HTTP requests from any public IP, including Alice’s.</li><li>CGNAT in AWS: Upon reaching AWS, Alice’s public IP-based request is translated to an internal private IP using Carrier-Grade NAT (CGNAT), allowing communication with Bob’s server.</li></ol><h4>Bob’s Server Processing</h4><ol><li>Data Reception: Bob’s server receives the request, where the Ethernet frame is stripped to reveal the IP datagram, which is then processed to retrieve the TCP segment.</li><li>TCP Reassembly: The server’s TCP stack reassembles segments using sequence numbers, ensuring the data is in the correct order and complete.</li><li>HTTP Request Processing: The HTTP request reaches the application layer on Bob’s server, where the requested webpage is prepared.</li></ol><h4>Response Journey Back to Alice</h4><ol><li>Response Creation: Bob’s server creates an HTTP response.</li><li>Encapsulation Reversed: The response goes through similar encapsulation — TCP segments, IP datagrams, and finally Ethernet frames.</li><li>Return Route: These frames travel back through AWS’s infrastructure, public internet, and Alice’s router.</li></ol><h4>Final Steps: Completing the Cycle</h4><ol><li>NAT at Alice’s Router: Alice’s router translates the public IP back to Alice’s private IP and forwards the Ethernet frame to Alice’s device.</li><li>Response Delivery: Alice’s device receives the frame, extracts the IP datagram and TCP segment, and finally, the HTTP response is rendered in her web browser.</li></ol><h3>Networking Concepts in the Alice and Bob Story</h3><h4>1. Private IP and Public IP</h4><ul><li>Private IP: Alice’s device has a private IP (192.168.1.101) used within her local network. It’s not routable on the internet.</li><li>Public IP: The router translates Alice’s private IP into a public IP to communicate over the internet.</li><li>Alternative: In some setups, VPNs could be used for secure communication over public networks while maintaining private IP addresses.</li></ul><h4>2. DNS Query and Cache</h4><ul><li>DNS Query: Alice’s device sends a DNS query to resolve Bob’s domain name to an IP address.</li><li>DNS Cache: The resolved IP address is stored locally to speed up future access.</li><li>Alternative: Sometimes, local hosts files might be used for small-scale, manual DNS-like functionality.</li></ul><h4>3. TCP Segmentation and Reassembly</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*hvZv03oOw8CXs7Hi" /></figure><ul><li>Segmentation: TCP breaks down data into manageable segments, each with a sequence number for orderly reassembly.</li><li>Reassembly: At the destination, TCP reassembles these segments in the correct order.</li></ul><h4>4. IP Datagram and Routing</h4><ul><li>IP Datagram: Each TCP segment is encapsulated in an IP datagram, containing source and destination IP addresses.</li><li>Routing: Routers direct the datagram through the network based on the destination IP.</li><li>Alternative: MPLS (Multiprotocol Label Switching) can be used in some networks for more efficient data routing.</li></ul><h4>5. Ethernet Frame and MAC Address</h4><ul><li>Ethernet Frame: The datagram is further encapsulated in an Ethernet frame, containing MAC addresses of source and destination hardware.</li><li>MAC Address: Unique to each device, used for local network communication.</li></ul><h4>6. NAT (Network Address Translation)</h4><figure><img alt="Image is by cloudflare" src="https://cdn-images-1.medium.com/max/1024/1*HPkIyQE3al-8ZG69UURidQ.png" /></figure><ul><li>NAT: Translates Alice’s private IP to a public IP for internet communication, and vice versa for incoming traffic. ( Why ? Here is a <a href="https://www.cloudflare.com/learning/dns/glossary/what-is-my-ip-address/">Link</a> by cloudflare explaining it)</li></ul><h4>7. VPC and Security Groups</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*RTUfwF7Lcjugfri4" /></figure><ul><li>VPC: Virtual Private Cloud, providing an isolated network within the cloud.</li><li>Security Groups: Act as firewalls to control inbound and outbound traffic.</li></ul><h4>8. Handshakes and Connection Establishment (Detailed Story)</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*siX2P_kyuDKLcXjB.png" /></figure><ul><li>Initiation: Alice’s browser initiates a connection to Bob’s server by sending a TCP segment with a SYN (synchronize) flag set, indicating the start of a new connection.</li><li>Acknowledgment: Bob’s server acknowledges this request by sending back a TCP segment with SYN-ACK (synchronize-acknowledge) flags set.</li><li>Final Agreement: Alice’s device responds with an ACK (acknowledge) flag set, completing the handshake and establishing the connection.</li><li>Purpose: This handshake ensures both parties are ready for communication and agree on parameters like sequence numbers.</li></ul><p><em>Basically translates to :</em></p><p><em>you good ?</em></p><p><em>I am good, you good ?</em></p><p><em>yeah, I am good.</em></p><h4>Congestion Control</h4><ul><li>TCP Congestion Control: Adjusts the rate of data transmission based on network capacity, preventing congestion.</li></ul><h4>10. Firewalls and Protocols</h4><ul><li>Firewalls: Software or hardware-based systems that enforce security rules.</li><li>Protocols: PPP (Point-to-Point Protocol) is an alternative for direct connections between two nodes, often used in WANs.</li></ul><h4>10. Firewalls and Protocols</h4><ul><li>Firewalls: Software or hardware-based systems that enforce security rules.</li><li>Protocols: PPP (Point-to-Point Protocol) is an alternative for direct connections between two nodes, often used in WANs.</li></ul><blockquote>Optional</blockquote><blockquote>Here are the networking types in brief:</blockquote><blockquote>PAN — Connects small personal devices within a short range LAN — Connects computers within a limited area like a building or office</blockquote><blockquote>CAN — Interconnects LANs into campus-wide network MAN — Covers a city area connecting nearby buildings</blockquote><blockquote>WAN — Network spans a large geographical region like countries</blockquote><blockquote>The types vary by coverage area from smallest to largest:</blockquote><blockquote>PAN &lt; LAN &lt; CAN &lt; MAN &lt; WAN</blockquote><blockquote>Key difference is the geographic span — from within feet between individual devices in a PAN to thousands of kilometers connecting nations across a WAN.</blockquote><h4>11. Stateful vs. Stateless</h4><ul><li>Stateful (Security Groups in AWS): These remember the state of previous packets, allowing or denying new packets based on established connections.</li><li>Stateless (IP Routing): Makes decisions based purely on the current packet, without memory of past interactions.</li></ul><h4>12. Fragments and Bytes</h4><ul><li>Fragments: Large IP datagrams can be broken into smaller fragments to fit the MTU (Maximum Transmission Unit) of the network.</li><li>Bytes: Each fragment and packet consists of bytes, the basic units of digital data.</li></ul><h4>13. Header Information</h4><ul><li>Metadata and Headers: Both TCP and IP headers contain critical information (like source/destination IP, port numbers, sequence numbers) essential for routing and reassembly of data.</li></ul><h4>14. Sequence Numbers</h4><ul><li>Role in TCP: Used to order segments correctly and ensure data integrity. Also crucial in congestion control and for recovery from packet loss.</li></ul><h4>15. Ethernet Frames</h4><ul><li>PPP Alternative: While Ethernet is dominant in LANs, PPP is used for point-to-point direct connections in WANs. PPP frames differ from Ethernet frames, particularly in how they encapsulate data.</li></ul><h4>16. Firewalls</h4><ul><li>Network Security: Firewalls act as barriers between networks, inspecting incoming and outgoing traffic based on predefined security rules.</li></ul><h4>17. Congestion Control</h4><ul><li>TCP’s Role: TCP dynamically adjusts the rate of data transmission to prevent congestion on the network, ensuring reliable and efficient communication.</li></ul><h4>18. Handshakes — A Closer Look</h4><ul><li>SYN-SYN-ACK-ACK Process: This is the cornerstone of establishing a reliable connection in TCP. It’s a mutual agreement on connection parameters and a check for the readiness of both parties to communicate.</li><li>UDP’s Lack of Handshake: UDP, being stateless and faster, skips this process, which is why it’s preferred for real-time applications where speed is more critical than reliability, like streaming services.</li></ul><p>Now, now this is actually very good, you have gotten this far. For the most part you are now more than functional. You want to learn more ?</p><p>Indeed, the initial story between Alice and Bob was based on TCP (Transmission Control Protocol). Let’s first explore the key differences between TCP and UDP (User Datagram Protocol), and then reimagine the story using UDP.</p><h3>TCP vs. UDP</h3><h4>TCP (Transmission Control Protocol)</h4><ul><li>Reliability: Ensures data delivery with acknowledgments and retransmissions if necessary.</li><li>Ordered Delivery: Maintains the order of data packets.</li><li>Error Checking: Performs extensive error checking and recovery.</li><li>Congestion Control: Adjusts transmission rate based on network capacity.</li><li>Handshake Protocol: Employs a three-way handshake (SYN, SYN-ACK, ACK) for establishing connections.</li><li>Use Cases: Ideal for applications where reliability is crucial, like web browsing, email, file transfers.</li></ul><h4>UDP (User Datagram Protocol)</h4><ul><li>Speed and Simplicity: Less overhead than TCP, faster transmission.</li><li>No Guarantee of Delivery: No acknowledgments or retransmissions.</li><li>No Ordered Delivery: Packets may arrive out of order.</li><li>No Congestion Control: Does not adjust for network capacity; can overwhelm network with rapid transmission.</li><li>Stateless: No connection establishment or maintenance.</li><li>Use Cases: Suited for real-time applications where speed is prioritized over reliability, such as live video streaming, online gaming, VoIP.</li></ul><p>## Comparison of TCP and UDP</p><p>Understanding the differences between TCP (Transmission Control Protocol) and UDP (User Datagram Protocol) is crucial for our story. Here’s a quick comparison:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*b1aXH6drRn2YOT602sImBw.png" /></figure><p>This table sets the stage for the nuances that will come into play in Alice and Bob’s digital communication adventure.</p><h3>The Story Reimagined with UDP</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*T9dgePNuISOaIfrV" /></figure><p>In this version, Alice is streaming a live video from Bob’s server, a scenario where UDP’s speed and real-time data transfer are more beneficial than the reliability offered by TCP.</p><h4>Alice’s Request</h4><ol><li>Streaming Intent: Alice wants to watch a live video hosted on Bob’s server.</li><li>Private IP and MAC Address: Alice’s device still has a private IP and a unique MAC address.</li></ol><h4>UDP in Action</h4><ol><li>No DNS Cache: Assuming the IP is known or resolved by another means (as UDP does not involve DNS caching).</li><li>Datagram Creation: Alice’s device sends a UDP datagram to request the stream. Unlike TCP, there is no segmentation or sequence numbers.</li><li>No Handshake: There’s no SYN-SYN-ACK handshake process. The request is sent directly to Bob’s server.</li></ol><h4>Data Transmission</h4><p>UDP Datagram and Routing: The UDP datagram, encapsulated in an IP packet and then an Ethernet frame, is sent through Alice’s router, which</p><p>performs NAT (replacing her private IP with its public IP) and sends it over the internet. 2. Speed over Reliability: The datagrams travel to Bob’s server without the need for acknowledgments or retransmissions. This means some packets might be lost or arrive out of order, but the stream continues uninterrupted, favoring real-time viewing over completeness.</p><p>No Congestion Control: UDP doesn’t adjust its sending rate based on network conditions. If the network is congested, packet loss may increase, affecting the quality of the stream.</p><h4>Bob’s Server Processing</h4><ol><li>Handling Datagram: Bob’s server receives the UDP datagrams. Since UDP doesn’t guarantee order, the server is designed to handle packets arriving out of sequence.</li><li>Stream Delivery: The server starts streaming the video. It sends out UDP datagrams containing the video data back to Alice.</li></ol><h4>Response Journey Back to Alice</h4><ol><li>Continuous Stream: Unlike TCP, there’s no error checking or retransmission from Bob’s server. Lost packets result in minor glitches in the stream rather than delays.</li><li>NAT at Alice’s Router: The datagrams are translated back to Alice’s private IP and delivered to her device.</li></ol><h4>Viewing Experience</h4><ol><li>Real-Time Streaming: Alice experiences the live stream with minimal buffering. However, she might notice occasional glitches due to packet loss.</li><li>Stateless Communication: The connection is not “maintained” in any way. If Alice pauses or stops the stream, there’s no formal “disconnection” process as in TCP.</li></ol><p>In short, The UDP version of the story highlights how UDP’s characteristics are suitable for applications where speed and real-time data transfer are more important than ensuring every single packet is received and in order</p><p>. This makes UDP ideal for live streaming, online gaming, and VoIP, where occasional packet loss is preferable to the latency that would be introduced by TCP’s reliability mechanisms.</p><h4>Key Takeaways in the UDP Story:</h4><ol><li>Efficiency and Speed: UDP’s lack of a connection establishment process, absence of acknowledgments, and simpler header structure contribute to faster data transmission.</li><li>Loss Tolerance: Applications using UDP are generally designed to tolerate some level of data loss. For instance, in video streaming, losing a few frames might not significantly impact the viewer’s experience.</li><li>No Congestion Handling: UDP does not slow down transmissions in response to network congestion, which can lead to increased packet loss but ensures continual data flow.</li><li>Stateless Nature: UDP does not track the state of the connection or the data being transmitted, making it suitable for broadcasting where data is sent to multiple recipients simultaneously.</li></ol><p>So, while TCP is about reliability and ordered delivery, UDP prioritizes speed and efficiency, making it the protocol of choice for time-sensitive applications where occasional data loss is an acceptable trade-off.</p><h3>A Story of Routing: Alice Sends an Email to Bob</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*uuif2Z4-Xe1rBp8s" /></figure><h4>The Setup: Alice’s Email</h4><p>Alice wants to send an email to Bob. Her email is a data packet with the address of Bob’s email server (let’s say bobsemail.com which translates to the IP address 93.184.216.34). Her own device has a private IP address (192.168.1.5), assigned by her router.</p><h4>Breaking Down the Data: MTU in Play</h4><p>Her email is a bit large, so her device breaks it down into smaller packets to comply with the MTU limit of her local network, which is 1500 bytes. Each packet is now small enough to travel without getting “stuck” due to size restrictions.</p><h4>Router’s Role with Addresses</h4><p>Alice’s router (Router A) has a public IP address (203.0.113.5) that represents Alice&#39;s network on the internet. It receives the packets and notes the destination address (93.184.216.34), which includes a route prefix (93.184) that indicates the general location of Bob&#39;s server in the network topology.</p><h4>The First Hop: Leaving the Local Network</h4><p>Router A sends the packets to the next hop, which is the ISP&#39;s router (Router B). Router B reads the route prefix and knows it needs to send the packets towards a specific direction, closer to Bob&#39;s server&#39;s network.</p><h4>Address Resolution and Further Hopping</h4><p>As packets move through the internet, each router along the way (Router C, Router D, etc.) uses the route prefix to forward the packets toward their destination. They don&#39;t need the exact address just yet, just like a letter doesn&#39;t need the exact house address until it gets to the local post office.</p><h4>Reaching Bob’s Network</h4><p>The packets finally arrive at the router (Router X) responsible for Bob&#39;s server network. Router X recognizes the full IP address and directs the packets to the specific server (93.184.216.34).</p><h4>Bob’s Server Response</h4><p>Bob’s server receives the packets, reassembles the</p><p>email, and sends an acknowledgment back to Alice. The acknowledgment packets have Alice’s router’s public IP address (203.0.113.5) as the destination.</p><h4>The Journey Back</h4><p>The acknowledgment takes a similar path back. Routers on the return path use the route prefix associated with Alice’s ISP to guide the packets. Each router examines the destination IP, updating the route as needed based on network conditions.</p><h4>Final Delivery</h4><p>The packets reach Alice’s router, which translates the public IP back to Alice’s private IP (192.168.1.5) using NAT, and delivers the acknowledgment to her device.</p><h3>More or less you get it now, But I mean, if you have more time ?</h3><p>Let’s dive a bit more into the application layer. Instead of HTTP now we will look into other types so, Let’s first briefly explain HTTPS and FTP and then retell Alice and Bob’s story to illustrate these protocols in action.</p><h3>HTTPS (Hypertext Transfer Protocol Secure)</h3><ul><li>What is HTTPS?: HTTPS is the secure version of HTTP, used for secure communication over a computer network, commonly the internet. It encrypts data to keep it confidential, which is crucial for sensitive transactions like online banking or shopping.</li></ul><p>When you visit a website with HTTPS, there is an extra protection added through something called SSL (Secure Sockets Layer). This does two main things:</p><ol><li>Encrypts the data being sent — So if anyone tries to intercept the traffic, they can’t read it because it appears scrambled and meaningless.</li><li>Verifies website identity — It makes sure you are sending data to and receiving data from exactly the website you tried to visit, and not some imposter.</li></ol><p>This prevents hackers from being able stealthily steal data or pretend to be another website.</p><ul><li>Encryption: It uses<strong> SSL/TLS</strong> protocol to encrypt the data during transit, ensuring that the information sent between the client and server is unreadable to anyone else.</li></ul><p>This is <strong><em>sufficient understanding</em></strong> of HTTPs for most cases, but if you want more, feel free to read this otherwise move on to FTP.</p><p>HTTPS uses TLS (Transport Layer Security) protocol to encrypt communication and provide authentication.</p><p>When you first visit a HTTPS website:</p><ol><li>Your browser requests the site’s SSL certificate containing its public key and identity.</li><li>The TLS handshake happens — The website sends its signed SSL certificate, and your browser verifies it matches the domain.</li><li>Your browser then uses the public key from the certificate to securely transmit a randomly generated symmetric session key to the server.</li><li>This encrypted exchange of the symmetric key leverages TLS to enable secure transmission of data going forward.</li><li>All further HTTP communication uses this symmetric session key and the TLS record protocol to encrypt application data flowing in each direction.</li></ol><p>So in summary:</p><ul><li>TLS enables authenticated key exchange used in HTTPS and encrypts the initial handshake.</li><li>The SSL certificate provides authentication and ensures the key exchange is with the right server.</li><li>Session keys are used for fast symmetric encryption of bulk application data sent after the handshake.</li><li>TLS provides the underlying authenticated and encrypted channels that SSL/HTTPS security builds on top of.</li><li>TLS is an upgraded version of SSL. It is essentially SSL version 3.1. TLS supports newer, stronger encryption algorithms and keys than SSL such as AES</li></ul><h3>FTP (File Transfer Protocol)</h3><ul><li>What is FTP?: FTP is a standard network protocol used for the transfer of computer files between a client and server on a computer network. It’s often used for <strong><em>uploading</em></strong> or <strong><em>downloading</em></strong> large files from a server.</li><li>Modes of Operation: FTP can operate in active and passive modes, which define how the connection between a client and server is established.</li></ul><h3>More….. Let’s code then.</h3><p>Again, if you’re in a hurry- you’re good at this point, but we can still do more if you want ? Perhaps a change of pace… Some code ?.</p><p>Using Python, we’ll focus on some of the most critical parts, such as making HTTP requests, handling DNS lookups, and demonstrating TCP connections.</p><h3>1. Making an HTTP Request (GET)</h3><p>Python’s requests library is commonly used for making HTTP requests. Here&#39;s a simple example of sending an HTTP GET request:</p><pre>import requests<br><br>url = &#39;http://example.com&#39;<br>response = requests.get(url)<br><br>print(response.text)  # Prints the HTML content of the requested page</pre><h3>2. DNS Lookup</h3><p>Python’s socket library can be used for DNS lookups to resolve hostnames to IP addresses:</p><pre>import socket<br><br>hostname = &#39;example.com&#39;<br>ip_address = socket.gethostbyname(hostname)<br><br>print(f&quot;The IP address of {hostname} is {ip_address}&quot;)</pre><h3>3. Establishing a TCP Connection</h3><p>Creating a basic TCP client in Python involves using the socket library. Here&#39;s a simple example to establish a TCP connection:</p><pre>import socket<br><br>host = &#39;example.com&#39;<br>port = 80  # HTTP port<br><br># Create a socket object with IPv4 addressing and TCP protocol<br>client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)<br><br># Connect to the server<br>client_socket.connect((host, port))<br>print(f&quot;Connected to {host} on port {port}&quot;)<br><br># Close the connection<br>client_socket.close()</pre><p>The decision to use a low-level TCP connection (as shown in point 3) versus an abstracted HTTP request (as in points 1 and 4) depends on your specific needs and the level of control required over the communication process. <strong>But then again I almost never use this and mainly use the requests library.</strong></p><h4>When to Use a Direct TCP Connection:</h4><ol><li>Custom Protocol Implementation: If you are working with a custom protocol that operates over TCP but isn’t HTTP.</li><li>Lower-Level Network Operations: For educational purposes or when you need to manipulate aspects of the TCP protocol, such as custom timeout settings, packet sizes, or direct streaming of data.</li><li>Non-HTTP Services: Interacting with services that don’t communicate over HTTP, like SMTP for email, FTP for file transfers, or other TCP-based protocols.</li></ol><h4>Example Scenario:</h4><ul><li>Direct TCP Connection: Suppose you’re developing a client that communicates with a custom TCP server for a real-time data streaming service. Using a direct TCP connection would be more appropriate as it gives you the ability to fine-tune the connection and data handling according to the protocol’s requirements.</li></ul><h3>4. HTTPS Request</h3><p>For HTTPS requests, Python’s requests library handles SSL/TLS encryption automatically:</p><pre>response = requests.get(&#39;https://example.com&#39;)<br>print(response.text)  # Prints the secure HTML content of the requested page</pre><h3>5. Using FTP</h3><p>Python’s ftplib library can be used for FTP operations. Here&#39;s an example of connecting to an FTP server:</p><pre>from ftplib import FTP<br><br>ftp_server = &#39;ftp.example.com&#39;<br>ftp = FTP(ftp_server)<br>ftp.login()  # login with anonymous user<br><br>ftp.retrlines(&#39;LIST&#39;)  # List directory contents<br><br>ftp.quit()</pre><p>It’s particularly useful for handling large files or batches of files, and for situations where HTTP might not be as efficient or appropriate. Here are some common use cases for FTP:</p><ol><li>Website Management: Uploading or downloading web files (like HTML, CSS, images) to and from a web server.</li><li>Data Transfer: Sharing or backing up large datasets that might be too big for email attachments or other methods.</li><li>Software Distribution: Distributing software and updates, especially in a corporate environment or for server-to-server transfers.</li></ol><p>4. Remote File Management: Managing files on a remote server, including renaming, deleting, or changing file permissions.</p><p>For example</p><pre>from ftplib import FTP<br><br>def download_file(ftp_server, username, password, remote_file_path, local_file_path):<br>    ftp = FTP(ftp_server)<br>    ftp.login(username, password)  # Replace with actual username and password<br><br>    with open(local_file_path, &#39;wb&#39;) as file:<br>        ftp.retrbinary(f&#39;RETR {remote_file_path}&#39;, file.write)<br><br>    ftp.quit()<br>    print(f&quot;Downloaded {remote_file_path} to {local_file_path}&quot;)<br><br># Example usage<br>download_file(&#39;ftp.example.com&#39;, &#39;user&#39;, &#39;pass&#39;, &#39;/path/to/remote/file.txt&#39;, &#39;local_file.txt&#39;)</pre><p>Here is most of the code that you will probably need</p><pre>from ftplib import FTP<br><br># Connect to an FTP server<br>def connect_to_ftp(host, username, password):<br>    ftp = FTP(host)<br>    ftp.login(username, password)<br>    return ftp<br><br># List files in the current directory on the server<br>def list_files(ftp):<br>    files = ftp.nlst()<br>    for file in files:<br>        print(file)<br><br># Upload a file to the server<br>def upload_file(ftp, file_path, server_path):<br>    with open(file_path, &#39;rb&#39;) as file:<br>        ftp.storbinary(f&#39;STOR {server_path}&#39;, file)<br>    print(f&quot;Uploaded {file_path} to {server_path}&quot;)<br><br># Download a file from the server<br>def download_file(ftp, server_path, local_path):<br>    with open(local_path, &#39;wb&#39;) as file:<br>        ftp.retrbinary(f&#39;RETR {server_path}&#39;, file.write)<br>    print(f&quot;Downloaded {server_path} to {local_path}&quot;)<br><br># Change directory on the server<br>def change_directory(ftp, path):<br>    ftp.cwd(path)<br>    print(f&quot;Changed directory to {path}&quot;)<br><br># Disconnect from the server<br>def disconnect_from_ftp(ftp):<br>    ftp.quit()<br><br># Main function to demonstrate FTP operations<br>def main():<br>    host = &quot;ftp.example.com&quot;<br>    username = &quot;user&quot;<br>    password = &quot;pass&quot;<br><br>    ftp = connect_to_ftp(host, username, password)<br><br>    print(&quot;Files in the root directory:&quot;)<br>    list_files(ftp)<br><br>    # Change to a specific directory<br>    change_directory(ftp, &#39;/path/to/directory&#39;)<br><br>    # Upload a file<br>    upload_file(ftp, &#39;local_file.txt&#39;, &#39;server_file.txt&#39;)<br><br>    # Download a file<br>    download_file(ftp, &#39;server_file.txt&#39;, &#39;downloaded_file.txt&#39;)<br><br>    # Disconnect<br>    disconnect_from_ftp(ftp)<br><br>if __name__ == &quot;__main__&quot;:<br>    main()</pre><h3>Breakdown of the FTP Example</h3><h4>Host</h4><ul><li>Host: The host is the address of the FTP server that you are trying to connect to. It can be a domain name (like ftp.example.com) or an IP address. It&#39;s similar to a physical address for a house; it tells your FTP client where to find the FTP server on the internet.</li></ul><h4>connect_to_ftp Function</h4><ul><li>Purpose: This function establishes a connection to the FTP server.</li><li>Parameters:</li><li>host: The address of the FTP server.</li><li>username and password: Credentials for logging into the FTP server. These are required because most FTP servers have some level of access control.</li></ul><h4>list_files Function</h4><ul><li>Purpose: Lists all files and directories in the current directory on the FTP server.</li><li>Implementation: Uses the nlst() method of the FTP object, which sends a command to the server to list directory contents.</li></ul><h4>upload_file Function</h4><ul><li>Purpose: Uploads a file from the local machine to the FTP server.</li><li>Parameters:</li><li>file_path: Path of the file on the local machine.</li><li>server_path: Path where the file will be stored on the server.</li><li>Implementation: Opens the local file in binary mode and uses storbinary to upload it, which reads the local file and sends it to the FTP server.</li></ul><h4>download_file Function</h4><ul><li>Purpose: Downloads a file from the FTP server to the local machine.</li><li>Parameters:</li><li>server_path: Path of the file on the server.</li><li>local_path: Path where the file will be saved on the local machine.</li><li>Implementation: Uses retrbinary to retrieve the file. It writes the data to the local file as it&#39;s received from the server.</li></ul><h4>change_directory Function</h4><ul><li>Purpose: Changes the current working directory on the FTP server.</li><li>Parameter:</li><li>path: The directory path on the server to change to.</li><li>Implementation: Uses cwd (change working directory) command to change the directory.</li></ul><h4>disconnect_from_ftp Function</h4><ul><li>Purpose: Closes the connection to the FTP server.</li><li>Implementation: Calls quit() on the FTP object, which properly closes the connection.</li></ul><h3>Now let us put things together</h3><p>I want you to think about creating a service for uploading and streaming videos involves several considerations, especially regarding the application layer and transport layer protocols. ….. Done thinking ?</p><p>Let’s outline how such a service might be structured, In a simplified scenario, you might use Python for the backend with Flask as a web framework, and an FTP server for file storage. For streaming, HTTP Live Streaming (<strong><em>HLS</em></strong>) can be an effective method.</p><p>[ <strong><em>But Omar I don’t know what HLS is and after all of this I would have thunk I would use UDP…. You’ve made it this far patience </em></strong>]</p><p>Below is a conceptual example of how this could be implemented First</p><h3>1. Flask Web Service for Video Upload</h3><blockquote>NOTE : In a real-world scenario, you might have multiple videos to upload, and you could implement batch processing or parallel uploads to improve efficiency. However, the core concept of uploading videos remains the same.</blockquote><p>I will be using flask for this</p><ul><li>Flask and os: These are Python modules. Flask is a web framework used to create web applications, and os is a module for interacting with the operating system.</li><li>Flask App Instance: app = Flask(__name__) creates an instance of the Flask class. This instance acts as the web application.</li></ul><p>Here’s how you might set up a Flask application to handle video uploads:</p><pre>from flask import Flask, request, jsonify, send_from_directory<br>import os<br><br>app = Flask(__name__)<br>UPLOAD_FOLDER = &#39;/path/to/uploaded/videos&#39;<br>app.config[&#39;UPLOAD_FOLDER&#39;] = UPLOAD_FOLDER<br><br>@app.route(&#39;/upload&#39;, methods=[&#39;POST&#39;])<br>def upload_file():<br>    if &#39;file&#39; not in request.files:<br>        return jsonify({&#39;error&#39;: &#39;No file part&#39;})<br>    <br>    file = request.files[&#39;file&#39;]<br>    if file.filename == &#39;&#39;:<br>        return jsonify({&#39;error&#39;: &#39;No selected file&#39;})<br>    <br>    if file:<br>        filename = file.filename<br>        filepath = os.path.join(app.config[&#39;UPLOAD_FOLDER&#39;], filename)<br>        file.save(filepath)<br>        return jsonify({&#39;success&#39;: f&#39;File {filename} uploaded successfully.&#39;})<br><br>if __name__ == &#39;__main__&#39;:<br>    app.run(debug=True)</pre><p>In this setup, the video is uploaded as a binary file through a multi-part form request, which is a standard way to upload files over HTTP. A binary file is a type of computer file that is not a text file. Unlike text files, which store data in a format that is readable by humans (like ASCII characters), binary files store data in binary form, which is typically readable only by computers.</p><p>Route Decorator: @app.route(&#39;/upload&#39;, methods=[&#39;POST&#39;]) tells Flask that this function (upload_file) should be called when a POST request is made to the /upload URL.</p><p>As for the actual process.</p><ul><li>Suppose an uploaded file video.mp4 is to be saved in the directory /uploads. If the UPLOAD_FOLDER in the Flask app is set to /uploads, the filepath would be /uploads/video.mp4.</li><li>Calling file.save(&#39;/uploads/video.mp4&#39;) would save the uploaded video file to that location on the server.</li></ul><blockquote>Networking Protocol for Upload: In the code provided, we are using HTTP for video uploads. HTTP (Hypertext Transfer Protocol) is a widely used protocol for data transfer over the web. It’s often used for file uploads because it’s supported by most web servers and can handle binary data like videos.</blockquote><blockquote>We are not using FTP (File Transfer Protocol) in this example for simplicity, but FTP is another protocol specifically designed for file transfers. FTP provides more features and control over file transfers than HTTP, making it a good choice for scenarios where you need fine-grained control, such as transferring large numbers of files or managing directories on a remote server. FTP is not as widely supported in web applications as HTTP, which is why HTTP is often preferred for simple file uploads.</blockquote><h3>2. File Transfer to Remote Server</h3><p>After the video is uploaded to the Flask server, it can be transferred to a remote server (e.g., FTP server) for storage:</p><pre>from ftplib import FTP<br><br>def transfer_to_ftp(filepath, filename):<br>    ftp_server = &#39;ftp.example.com&#39;<br>    username = &#39;user&#39;<br>    password = &#39;password&#39;<br>    <br>    ftp = FTP(ftp_server)<br>    ftp.login(username, password)<br><br>    with open(filepath, &#39;rb&#39;) as file:<br>        ftp.storbinary(f&#39;STOR {filename}&#39;, file)<br>    <br>    ftp.quit()</pre><p>This function should be called after the file is saved in the upload_file endpoint.</p><h3>3. Video Streaming Service</h3><p>To stream videos, you’ll need to convert them into a format suitable for streaming (like HLS) and then serve them via HTTP. This part can be complex and might involve using additional tools like FFmpeg for video processing and a more advanced setup for serving the content.</p><p>A simple example to serve video files using Flask:</p><pre>@app.route(&#39;/video/&lt;filename&gt;&#39;)<br>def stream_video(filename):<br>    return send_from_directory(app.config[&#39;UPLOAD_FOLDER&#39;], filename)</pre><p>This example provides a basic framework for a service that uploads videos and allows them to be streamed. In a real-world scenario, you’d need to consider many more details, such as robust error handling, data encryption, efficient storage and retrieval, video transcoding for different resolutions, and bandwidth optimization. Additionally, the actual streaming of video would likely be done using specialized streaming servers or services that can handle the demands of video streaming better than a simple Flask app.</p><p>For a scalable, production-grade system, you would explore cloud-based solutions like AWS S3 for storage, Elastic Transcoder for video processing, and CloudFront for content delivery. These services provide the necessary infrastructure to handle large-scale video streaming applications.</p><p>Now back to the choice of <strong>HLS</strong> over <strong>UDP</strong></p><h4>HLS (HTTP Live Streaming)</h4><ul><li>Protocol Layer: HLS operates at the application layer and is built on top of HTTP.</li><li>How It Works: HLS streams multimedia content using a series of small HTTP-based file downloads. It breaks the overall stream into a sequence of small HTTP-based file segments, each containing a short interval of playback time.</li><li>Adaptive Streaming: HLS supports adaptive bitrate streaming, meaning it can adjust the quality of video streams in real time based on the viewer’s available bandwidth and device capabilities. <strong>(the Settings button on YouTube)</strong></li><li>Compatibility and Reach: HLS is widely supported on various platforms and devices, including browsers, smartphones, and smart TVs.</li><li>Reliability: Since HLS uses HTTP, it benefits from the reliability of TCP (Transmission Control Protocol), ensuring that all data packets are delivered correctly and in order as opposed to <strong>UDP</strong></li></ul><h4>So to answer the question you never asked, why Not Use UDP for HLS?</h4><ul><li>Reliability over Speed: For many streaming applications, particularly those delivered over the internet to a diverse range of devices, the reliability and adaptability of HLS outweigh the speed advantage of UDP. HLS ensures that all viewers receive a continuous and adaptable stream, regardless of their network conditions.</li><li>Network Compatibility: UDP can face challenges with firewalls and network address translation (NAT), as many networks are optimized for TCP-based traffic.</li><li>Error Correction: HLS’s use of TCP allows for error correction, ensuring that the video and audio data are received correctly, which is crucial for providing a smooth viewing experience.</li><li>Browser Support: HLS is widely supported in web browsers and does not require additional plugins or software, unlike some UDP-based streaming solutions.</li></ul><p>Besides UDP and FTP (File Transfer Protocol), as well as its secure version SFTP (SSH File Transfer Protocol), still have their use cases, but they are less common in modern media streaming and content delivery scenarios, especially for large-scale streaming services. <strong>BUT MAKE NO MISTAKE UDP is Faster so it is up to you to make that judgment call if need be.</strong></p><h4>Troubleshooting on MAC</h4><p>Troubleshooting network connectivity issues in the terminal on a Mac can be a useful skill for diagnosing and resolving networking problems. Here are some common troubleshooting steps you can perform using terminal commands:</p><p><strong>Check Network Interfaces</strong></p><ul><li>To view a list of available network interfaces, open Terminal and type:</li><li>This command will display information about each network interface, including their IP addresses and status.</li></ul><pre>ifconfig</pre><p><strong>Check IP Configuration</strong></p><ul><li>To check the IP configuration of a specific network interface (e.g., Wi-Fi or Ethernet), use the ifconfig command followed by the interface name. For example:</li></ul><pre>ifconfig en0</pre><p><strong>Ping Test</strong></p><ul><li>You can use the ping command to test network connectivity to a remote host. For example:</li></ul><p>This command sends ICMP echo requests to the specified host (in this case, google.com) and displays the response time.</p><pre>ping google.com</pre><p>try -c as an argument, see what happens.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/524/1*8qZwznYxgEUtfiI-hGMzFA.png" /></figure><p>DNS Resolution:</p><ul><li>To troubleshoot DNS resolution issues, you can use the nslookup or dig command to query DNS servers. For example:</li></ul><pre>dig google.com</pre><p>Traceroute</p><p>traceroute is a network diagnostic tool that helps you trace the route that packets take from your computer to a destination host or IP address. It provides information about each intermediate router or hop along the path and measures the time it takes for packets to reach each hop. This can be useful for diagnosing network latency or routing issues. Here&#39;s how to use traceroute in the terminal on a Mac:</p><pre>traceroute [destination]<br><br>for example traceroute etherealai.io</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/570/1*YkbKwTB1sWtFwcz2UOIxuw.png" /></figure><h3>Finally</h3><p>here’s a table summarizing the commonly used networking protocols and technologies in modern applications, including web services, media streaming, and various other scenarios:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*R6iNbQ0c2wVmcwb14DAnaA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1013/1*6_v2JtATwN1HaIrTVJ2iQQ.png" /></figure><p>NSlook up</p><pre>nslookup [domain or IP]</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Ua72d7kY8UIVNH5BzH8Dvg.gif" /></figure><p>A while ago, I saw this Gif on linkedin</p><h3>Thank you for reading</h3><p>Images &amp; References:</p><ul><li><a href="https://en.wikipedia.org/wiki/Client%E2%80%93server_model#/media/File:Client-server-model.svg">https://en.wikipedia.org/wiki/Client%E2%80%93server_model#/media/File:Client-server-model.svg</a></li><li><a href="https://www.freecodecamp.org/news/what-is-http/">https://www.freecodecamp.org/news/what-is-http/</a></li><li><a href="https://www.cloudflare.com/learning/ddos/glossary/tcp-ip/">https://www.cloudflare.com/learning/ddos/glossary/tcp-ip/</a></li><li><a href="https://www.cloudflare.com/learning/dns/glossary/what-is-my-ip-address/">https://www.cloudflare.com/learning/dns/glossary/what-is-my-ip-address/</a></li><li><a href="https://eu.dlink.com/uk/en/support/faq/routers/how-do-i-find-my-ip-address-in-windows-vista-or-windows-7">https://eu.dlink.com/uk/en/support/faq/routers/how-do-i-find-my-ip-address-in-windows-vista-or-windows-7</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=51455db8b72a" width="1" height="1" alt=""><hr><p><a href="https://medium.com/the-modern-scientist/networking-in-a-hurry-51455db8b72a">Networking in a Hurry 💨</a> was originally published in <a href="https://medium.com/the-modern-scientist">The Modern Scientist</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Relational Inductive Biases, Deep Learning, and Graph Networks by Battaglia et al. (2018) Overview]]></title>
            <link>https://omar-hussein.medium.com/relational-inductive-biases-deep-learning-and-graph-networks-by-battaglia-et-al-2018-overview-8c4fc89395bb?source=rss-8fb24e986396------2</link>
            <guid isPermaLink="false">https://medium.com/p/8c4fc89395bb</guid>
            <category><![CDATA[gnn]]></category>
            <category><![CDATA[neural-networks]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[graph-neural-networks]]></category>
            <dc:creator><![CDATA[Omar Hussein]]></dc:creator>
            <pubDate>Mon, 10 Jul 2023 18:29:19 GMT</pubDate>
            <atom:updated>2023-07-10T18:29:19.699Z</atom:updated>
            <content:encoded><![CDATA[<p>The paper aims to provide a unifying framework to bridge the gap between graph-based and deep learning methods. The authors argue that graph networks (GNs) offer a flexible approach to merge these techniques, as they provide a strong relational inductive bias that can handle a wide range of problems and data types.</p><p>Here are the main concepts:</p><ol><li>Relational Inductive Biases: An inductive bias is an assumption made by a learning algorithm that guides it towards a specific solution when faced with more than one solution. Relational inductive bias is the assumption that the relationships between entities (i.e., the graph structure) are important for learning.</li><li>Graph Networks (GNs): The authors introduce a class of models called Graph Networks, which can reason about graph-structured data. GNs can be thought of as a generalization of both Convolutional Neural Networks (CNNs) and Recurrent Neural Networks (RNNs) to graph-structured data. The authors present a general GN block, which takes a graph as input and returns a graph as output. The GN block comprises three stages: edge update, node update, and global update. In each stage, information is aggregated from the relevant elements (edges, nodes, or the whole graph), transformed via a “model” function, and then combined with the existing features.</li><li>Graph Representations: The authors use a graph representation that includes global attributes (features of the entire graph), node attributes (features of individual nodes), and edge attributes (features of individual edges). This is a more general representation than those used in standard graph theory, which typically includes only nodes and edges.</li><li>Learning and Generalization: The authors also explore how GNs can be used for learning and generalization. They argue that GNs have strong generalization capabilities due to their relational inductive bias. GNs can be used to learn on one type of graph and generalize to other types of graphs with similar structural properties.</li><li>Applications: The paper shows how GNs can be applied to a wide range of problems, including physical systems, combinatorial optimization, multi-agent systems, and social and biological networks.</li></ol><p>— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —</p><p>Global features in a Graph Network:</p><ol><li>The global features, denoted by u in the paper, capture information about the entire graph. These are attributes or characteristics that represent the graph as a whole, not just individual nodes or edges. For example, in a social network graph, a global feature could be the overall density of the graph, the average number of friends per user, or the total number of communities.</li><li>The calculation of these global features depends on the specific problem and the data you’re dealing with. In many cases, they are computed as a function of node and edge attributes. In the Graph Network (GN) block described in the paper, the global features are updated in the “global update” stage. Here, information from the nodes and edges is aggregated (for example, by summing or averaging), and then combined with the existing global features using a learnable function. This update process allows the global features to capture complex characteristics of the graph structure and the node and edge features.</li><li>Process of a Graph Network with an example:</li><li>Let’s take the example of a social network graph, where nodes represent people, edges represent friendships, and we have a global attribute representing the overall density of the network.</li></ol><ul><li>Edge Update: In this stage, the attributes of each edge are updated based on the attributes of the edge itself, the attributes of the nodes it connects (source and target nodes), and the global attribute. For example, if edge attributes represent the strength of friendship, this could be updated based on the individual’s attributes (like common interests), and the overall density of the network.</li><li>Node Update: Here, the attributes of each node are updated based on the node’s own attributes, the updated attributes of the edges connected to it, and the global attribute. For instance, a person’s “influence score” could be updated based on their individual characteristics, the strength of their friendships, and the overall network density.</li><li>Global Update: Finally, the global attributes are updated based on the updated node and edge attributes. In our example, the overall network density could be updated based on the updated friendship strengths and individual influence scores.</li><li>The output of the GN block is a new graph where the nodes, edges, and global attributes have all been updated.</li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8c4fc89395bb" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Graph Neural Networks Series | Part 4 |The GNNs, Message Passing & Over-smoothing]]></title>
            <link>https://medium.com/the-modern-scientist/graph-neural-networks-series-part-4-the-gnns-message-passing-over-smoothing-e77ffee523cc?source=rss-8fb24e986396------2</link>
            <guid isPermaLink="false">https://medium.com/p/e77ffee523cc</guid>
            <category><![CDATA[gnn]]></category>
            <category><![CDATA[deep-learning]]></category>
            <category><![CDATA[message-passing]]></category>
            <category><![CDATA[graph]]></category>
            <category><![CDATA[graph-neural-networks]]></category>
            <dc:creator><![CDATA[Omar Hussein]]></dc:creator>
            <pubDate>Tue, 16 May 2023 17:11:04 GMT</pubDate>
            <atom:updated>2023-05-17T15:44:52.100Z</atom:updated>
            <content:encoded><![CDATA[<h3>Introduction</h3><p>In the first part of this series, we focused on learning low-dimensional embeddings for nodes in a graph using shallow embedding approaches. These approaches optimized individual embedding vectors for each node. However, in this article, we shift our attention to more complex encoder models specifically designed for graph data.</p><p><strong>Graph Neural Networks (GNNs)</strong> serve as a general framework for defining deep neural networks on graph-structured data. The main idea behind GNNs is to generate node representations that consider both the graph’s structure and any available feature information.</p><blockquote><strong><em>Optional point</em></strong></blockquote><blockquote>One possible approach is to use the adjacency matrix as input for a deep neural network. For example, we can flatten the adjacency matrix A and feed it into a Multi-Layer Perceptron (MLP) to obtain an embedding of the entire graph: z_G = MLP(A[1] ⊕ A[2] ⊕ … ⊕ A[|V|]) Here, A[i] represents a row of the adjacency matrix A, and ⊕ denotes vector concatenation.</blockquote><blockquote>z_G = MLP(A[1] ⊕ A[2] ⊕ … ⊕ A[|V|])</blockquote><blockquote>z_G: The embedding vector representing the entire graph.</blockquote><blockquote>MLP: Multi-Layer Perceptron, a type of neural network architecture.</blockquote><blockquote>A[i]: The i-th row of the adjacency matrix A, which represents the connections between nodes in the graph.</blockquote><blockquote>⊕: Vector concatenation operator, used to combine the row vectors into a single input vector for the MLP.</blockquote><blockquote>|V|: The total number of nodes in the graph.</blockquote><blockquote>However, this approach relies on the arbitrary ordering of nodes in the adjacency matrix, making it non-permutation invariant.</blockquote><blockquote>To address this limitation, we aim for permutation invariance or equivariance in the model. Permutation invariance means that the function’s output remains the same regardless of the ordering of rows or columns in the adjacency matrix: f(PAP^T) = f(A) (Permutation Invariance)</blockquote><blockquote>Equation 5.2: f(PAP^T) = f(A) (Permutation Invariance)</blockquote><blockquote>f: The function that takes the adjacency matrix A as input and produces an output.</blockquote><blockquote>P: Permutation matrix that represents the reordering of rows/columns in the adjacency matrix.</blockquote><blockquote>^T: Transpose operation, used to transpose the adjacency matrix.</blockquote><blockquote>Permutation equivariance ensures that the output is consistently permuted when the adjacency matrix is permuted: f(PAP^T) = Pf(A) (Permutation Equivariance)</blockquote><blockquote>Here, P is a permutation matrix.</blockquote><blockquote>f: The function that takes the adjacency matrix A as input and produces an output.</blockquote><blockquote>P: Permutation matrix that represents the reordering of rows/columns in the adjacency matrix.</blockquote><blockquote>Pf(A): The output of the function f after applying the permutation P to the output.</blockquote><blockquote>By designing models that exhibit permutation equivariance or invariance, we can effectively capture the structure and features of graphs, enabling powerful deep learning techniques for graph data analysis.</blockquote><h4>Why are GNNs “Hard” ?</h4><p>Unfortunately you cannot just throw jello on the wall and hope it sticks, that is just not how GNNs work. Developing complex encoders for graph data poses a challenge because traditional deep learning techniques are not directly applicable. Convolutional Neural Networks (CNNs) work well on grid-structured inputs like images, while Recurrent Neural Networks (RNNs) are suitable for sequential data such as text. To define deep neural networks on general graphs, a new type of architecture is needed.</p><h4>Neural Message Passing</h4><p>Neural Message Passing is a crucial concept in Graph Neural Networks (GNNs) because it enables information exchange and aggregation among nodes in a graph. In many real-world applications, data is structured as graphs, where entities are represented as nodes, and relationships between entities are represented as edges. Examples include social networks, biological networks, recommendation systems, and knowledge graphs.</p><p>The key challenge in modeling graph-structured data is effectively capturing the dependencies and interactions among nodes. Traditional deep learning techniques, such as convolutional neural networks (CNNs) for images or recurrent neural networks (RNNs) for sequences, are not directly applicable to graphs. Graphs lack the inherent grid structure of images or the linear order of sequences.</p><p>Neural Message Passing addresses this challenge by providing a framework for modeling dependencies and interactions in graph data. It allows nodes to exchange information with their neighboring nodes and aggregate that information to update their own representations. This message passing process is akin to nodes in a graph exchanging messages or signals, hence the name “Neural Message Passing.”</p><p>By passing messages through the graph, GNNs can leverage the local neighborhood information of each node to refine and update their representations. This enables GNNs to capture both the structural information of the graph and the features associated with each node. As a result, GNNs can learn rich and expressive representations of nodes that incorporate both local and global context.</p><p>Neural Message Passing has gained significant attention in the field of graph representation learning due to its ability to model complex relationships and dependencies in graph-structured data. It provides a powerful framework for solving various graph-related tasks, such as node classification, link prediction, graph classification, and recommendation.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/722/1*kgBSH3YEdyFKg8uSpYxCtA.png" /></figure><p>At each iteration of the message passing in a GNN, a hidden embedding h(k)_u is updated for each node u based on information gathered from its graph neighborhood N(u). This update can be expressed as follows:</p><p>h(k+1)_u = UPDATE(k)(h(k)_u, AGGREGATE(k)({h(k)_v, ∀v ∈ N(u)}))</p><p>Here, UPDATE and AGGREGATE are arbitrary differentiable functions, typically implemented as neural networks. The AGGREGATE function takes the embeddings of nodes in the neighborhood N(u) and generates a message m(k)_N(u) based on this aggregated information. The UPDATE function combines the message m(k)_N(u) with the previous embedding h(k-1)_u to produce the updated embedding h(k)_u. The initial embeddings at iteration k=0 are set to the input features xu for all nodes u. After K iterations of message passing, we can use the final layer output to define the embeddings for each node zu.</p><p>It’s important to note that GNNs are permutation equivariant by design, as the AGGREGATE function operates on a set of node embeddings, making the model invariant to the ordering of nodes.</p><p>Regarding node features, GNNs require input node features xu, ∀u ∈ V, unlike the shallow embedding methods discussed earlier in the series. In cases where rich node features are available, such as gene expression features or text features, they can be utilized. However, if no node features are present, alternative options can be employed. One option is to use node statistics</p><h4>Pseudocode</h4><p>Here are the steps of the Neural message passing broken down.</p><p>Each node in the graph receives an initial embedding, which can be the node’s input features.</p><ul><li>PyTorch Geometric: Assign initial node embeddings using the x attribute of the Data object.</li></ul><p>During each iteration of message passing:</p><ul><li>The node aggregates information from its neighboring nodes.</li><li>PyTorch Geometric: Use the MessagePassing class to define the message passing operation and aggregate messages from neighboring nodes.</li></ul><p>This aggregated information is combined with the node’s current embedding using an update function.</p><ul><li>PyTorch Geometric: Implement the update function within the MessagePassing class to combine the aggregated messages with the current node embeddings.</li></ul><p>The node’s embedding is updated based on the combined information.</p><ul><li>PyTorch Geometric: Implement the message function within the MessagePassing class to update the node embeddings based on the combined information.</li></ul><p>The updated embeddings are passed to the next iteration of message passing.</p><ul><li>PyTorch Geometric: Repeat the message passing process for multiple iterations using the propagate method of the MessagePassing class.</li></ul><p>After a certain number of iterations, the final embeddings are used to represent the nodes in the graph.</p><ul><li>PyTorch Geometric: Retrieve the final node embeddings from the x attribute of the Data object.</li></ul><p>The aggregation, update, and iteration steps are performed by neural networks, which can learn to capture complex patterns in the graph.</p><ul><li>PyTorch Geometric: Define the neural network models for the aggregation and update functions, which will be used within the MessagePassing class.</li></ul><p>The resulting node embeddings can be used for various downstream tasks, such as node classification or link prediction.</p><ul><li>PyTorch Geometric: Utilize the generated node embeddings for downstream tasks, such as feeding them into a classifier or predictor for node-related tasks.</li></ul><h4>Code</h4><p>This code looks different but it is the same concept do not be alarmed.</p><pre>import torch<br>import torch.nn as nn</pre><pre>class GNNLayer(nn.Module):<br>    def __init__(self, input_dim, output_dim):<br>        super(GNNLayer, self).__init__()<br>        self.update_fn = nn.Linear(input_dim, output_dim)<br>        self.aggregate_fn = nn.Linear(input_dim, output_dim)</pre><pre>    def forward(self, h, adj_matrix):<br>        messages = torch.matmul(adj_matrix, h)  # Aggregating messages from neighbors<br>        aggregated = self.aggregate_fn(messages)  # Applying the aggregate function<br>        updated = self.update_fn(h) + aggregated  # Updating the node embeddings<br>        return updated</pre><pre>class GNN(nn.Module):<br>    def __init__(self, input_dim, hidden_dim, output_dim, num_layers):<br>        super(GNN, self).__init__()<br>        self.layers = nn.ModuleList()<br>        self.layers.append(GNNLayer(input_dim, hidden_dim))<br>        for _ in range(num_layers - 2):<br>            self.layers.append(GNNLayer(hidden_dim, hidden_dim))<br>        self.layers.append(GNNLayer(hidden_dim, output_dim))</pre><pre>    def forward(self, features, adj_matrix):<br>        h = features.clone()<br>        for layer in self.layers:<br>            h = layer(h, adj_matrix)<br>        return h</pre><pre># Example usage<br>input_dim = 16<br>hidden_dim = 32<br>output_dim = 8<br>num_layers = 3</pre><pre># Generating random node features and adjacency matrix<br>num_nodes = 10<br>features = torch.randn(num_nodes, input_dim)<br>adj_matrix = torch.randn(num_nodes, num_nodes)</pre><pre># Creating a GNN model<br>gnn = GNN(input_dim, hidden_dim, output_dim, num_layers)</pre><pre># Forward pass through the GNN<br>embeddings = gnn(features, adj_matrix)</pre><pre>print(&quot;Node embeddings:&quot;)<br>print(embeddings)</pre><p>In this code, we define a simplified GNN with GNNLayer as the building block. Each GNNLayer consists of an update function and an aggregate function implemented as linear layers (nn.Linear). The forward pass of GNNLayer takes the node embeddings h and the adjacency matrix adj_matrix as inputs, aggregates messages from neighboring nodes, applies the aggregate function, and updates the node embeddings.</p><p>In the provided implementation, the update_fn and aggregate_fn functions in the GNNLayer class are linear neural networks (nn.Linear modules) instead of the simple addition operation used in the previous examples. This is because the linear layers allow for more flexibility and capacity in learning the transformation functions during the message passing process.</p><p>Using linear layers enables the GNN to learn complex non-linear relationships between nodes and their neighborhoods. The linear transformation applied by the neural networks allows the model to capture higher-order interactions and capture more intricate patterns in the graph structure.</p><p>By using neural networks as the update and aggregate functions, the GNN can adapt and learn different transformation functions for different layers. This flexibility allows the model to capture more expressive representations of the nodes and better model the graph structure.</p><blockquote>using linear neural networks in the update and aggregate functions of the GNN allows the model to approximate the underlying patterns and relationships present in the graph data. By using neural networks, the GNN can learn non-linear transformations that can better capture the complex structure and interactions within the graph. The <em>GNN</em> class represents the entire GNN model composed of multiple layers. In the forward pass, the node embeddings are propagated through the GNN layers, and the final layer&#39;s output represents the node embeddings.</blockquote><p>To demonstrate the usage, we create random node features and an adjacency matrix. Then, we instantiate a GNN model with the specified input, hidden, and output dimensions and the number of layers. Finally, we pass the features and adjacency matrix through the GNN model, resulting in node embeddings.</p><p>In the example code provided, the number of layers in the GNN model is determined by the num_layers parameter.</p><p>In the given code snippet, the num_layers is set to 3, which means there are 3 GNN layers in total. The first layer takes the input node features and transforms them to the hidden dimension (hidden_dim). The subsequent layers take the hidden dimension as input and apply the GNN operations iteratively until reaching the final layer, which outputs node embeddings of dimension output_dim.</p><p>You can adjust the value of num_layers to add or remove layers in the GNN model according to your specific needs.</p><h4>Node vs. graph-level equations provide different ways to define operations in graph neural networks (GNNs). Here’s a simplified explanation</h4><p><strong>Node-level equations</strong>: In GNNs, we often define the core message-passing operations at the node level. This approach involves each node aggregating information from its neighbors.</p><pre>import torch<br>import torch.nn as nn</pre><pre>class GNNLayer(nn.Module):<br>    def __init__(self, input_dim, output_dim):<br>        super(GNNLayer, self).__init__()<br>        self.update_fn = nn.Linear(input_dim, output_dim)<br>        self.aggregate_fn = nn.Linear(input_dim, output_dim)</pre><pre>    def forward(self, h, adj_matrix):<br>        messages = torch.matmul(adj_matrix, h)  # Aggregating messages from neighbors<br>        aggregated = self.aggregate_fn(messages)  # Applying the aggregate function<br>        updated = self.update_fn(h) + aggregated  # Updating the node embeddings<br>        return updated</pre><pre># Example usage<br>input_dim = 16<br>output_dim = 8<br>num_nodes = 10<br>features = torch.randn(num_nodes, input_dim)<br>adj_matrix = torch.randn(num_nodes, num_nodes)</pre><pre>gnn_layer = GNNLayer(input_dim, output_dim)<br>embeddings = gnn_layer(features, adj_matrix)</pre><p><strong>Graph-level equations</strong>: Some GNNs can be defined using graph-level equations, which provide a more concise representation. These equations operate on the entire graph rather than individual nodes.</p><pre>import torch<br>import torch.nn as nn</pre><pre>class GNN(nn.Module):<br>    def __init__(self, input_dim, output_dim):<br>        super(GNN, self).__init__()<br>        self.linear = nn.Linear(input_dim, output_dim)</pre><pre>    def forward(self, h, adj_matrix):<br>        updated = self.linear(torch.matmul(adj_matrix, h))<br>        return updated</pre><pre># Example usage<br>input_dim = 16<br>output_dim = 8<br>num_nodes = 10<br>features = torch.randn(num_nodes, input_dim)<br>adj_matrix = torch.randn(num_nodes, num_nodes)</pre><pre>gnn = GNN(input_dim, output_dim)<br>embeddings = gnn(features, adj_matrix)</pre><p>In both examples, the node-level and graph-level equations are implemented using PyTorch. The node-level equation involves aggregating messages from neighbors and updating node embeddings using neural network layers. The graph-level equation directly operates on the entire graph by applying linear transformations.</p><h3>self-loops</h3><p>Adding self-loops to the input graph and simplifying the message passing by aggregating information from the node’s neighbors as well as the node itself can be helpful for several reasons:</p><ol><li>Implicit Update: With self-loops, there is no need for an explicit update function. The update step is implicitly defined through the aggregation method. This simplification can make the model more concise and alleviate overfitting.</li><li>Parameter Sharing: Adding self-loops in the case of the basic GNN is equivalent to sharing parameters between the matrices responsible for self-information (Wself) and neighbor information (Wneigh). This parameter sharing can reduce the number of trainable parameters in the model and improve computational efficiency.</li><li>Regularization: Self-loops can provide regularization effects by incorporating the node’s own information in the aggregation process. This regularization can enhance the model’s generalization capabilities and prevent overfitting.</li></ol><p>However, it’s important to note that this simplification comes at the cost of limiting the expressivity of the GNN. By treating the node’s own information and the neighbor information equally, the model may struggle to differentiate between the two sources of information. This limitation should be considered when applying the simplified message passing approach.</p><pre>import torch<br>import torch.nn as nn</pre><pre>class GNN(nn.Module):<br>    def __init__(self, input_dim, output_dim):<br>        super(GNN, self).__init__()<br>        self.linear = nn.Linear(input_dim, output_dim)</pre><pre>    def forward(self, h, adj_matrix):<br>        adj_matrix_with_self = adj_matrix + torch.eye(adj_matrix.size(0))<br>        updated = self.linear(torch.matmul(adj_matrix_with_self, h))<br>        return updated</pre><pre># Example usage<br>input_dim = 16<br>output_dim = 8<br>num_nodes = 10<br>features = torch.randn(num_nodes, input_dim)<br>adj_matrix = torch.randn(num_nodes, num_nodes)</pre><pre>gnn = GNN(input_dim, output_dim)<br>embeddings = gnn(features, adj_matrix)</pre><p>In this modified code, adj_matrix_with_self is created by adding an identity matrix (torch.eye(adj_matrix.size(0))) to the original adjacency matrix (adj_matrix). This addition of self-loops ensures that each node includes its own information during the message passing process.</p><p>You can consider adding self-loops to the adjacency matrix when you want to incorporate a node’s own information into the message passing process. Here are a few scenarios where adding self-loops can be beneficial:</p><ol><li>Node Self-Information: If the information of a node itself is relevant and should influence its own embedding, adding self-loops allows the node to aggregate information from its neighbors as well as itself. This can be useful when the node’s own attributes or characteristics play a significant role in the task at hand.</li><li>Regularization: Adding self-loops can act as a form of regularization by including the node’s own information during message passing. This can help prevent overfitting and enhance the model’s generalization capabilities.</li><li>Graph Structure Importance: In certain graph-based tasks, capturing the overall graph structure and preserving connectivity patterns is crucial. By including self-loops, you ensure that each node considers its own connections when aggregating information, leading to better preservation of the graph structure.</li></ol><p>It’s important to note that not all GNN architectures or tasks require the addition of self-loops. It depends on the specific characteristics of your dataset and the nature of the task you are trying to solve. It is often a hyperparameter choice that you can experiment with and evaluate based on the performance and behavior of your model.</p><h3>Neighborhood aggregation methods</h3><p>The basic GNN model can be improved and generalized by exploring different neighborhood aggregation methods. In this section, we discuss the concept of neighborhood normalization to address the instability and sensitivity to node degrees in the basic aggregation operation. Normalization ensures that the aggregation is not biased towards nodes with higher degrees.</p><p>Neighborhood Normalization:</p><ul><li>The basic aggregation operation sums the neighbor embeddings but can be unstable and sensitive to node degrees.</li><li>One solution is to normalize the aggregation based on the degrees of the nodes involved.</li><li>The simplest approach is to take an average of the embeddings.</li><li>Another successful normalization method is symmetric normalization, which considers the degrees of both the node being aggregated and its neighbors.</li></ul><p>Graph Convolutional Networks (GCNs):</p><ul><li>One popular GNN model, the graph convolutional network (GCN), employs symmetric normalization and self-loop update.</li><li>The message passing function in GCN combines symmetric-normalized aggregation and an elementwise non-linearity.</li></ul><p>To Normalize or Not to Normalize?</p><ul><li>Proper normalization is essential for stable and strong GNN performance.</li><li>However, normalization can lead to a loss of information and can obscure structural graph features.</li><li>The decision to use normalization depends on the specific application and the balance between the importance of node features and structural information.</li></ul><p>Code examples:</p><pre>import torch<br>import torch.nn as nn</pre><pre>class GNN(nn.Module):<br>    def __init__(self, input_dim, output_dim):<br>        super(GNN, self).__init__()<br>        self.linear = nn.Linear(input_dim, output_dim)</pre><pre>    def forward(self, h, adj_matrix):<br>        normalized_agg = torch.matmul(adj_matrix, h) / adj_matrix.sum(dim=1, keepdim=True)<br>        updated = self.linear(normalized_agg)<br>        return updated</pre><pre># Example usage<br>input_dim = 16<br>output_dim = 8<br>num_nodes = 10<br>features = torch.randn(num_nodes, input_dim)<br>adj_matrix = torch.randn(num_nodes, num_nodes)</pre><pre>gnn = GNN(input_dim, output_dim)<br>embeddings = gnn(features, adj_matrix)</pre><p>In this example, we normalize the aggregation by dividing the adjacency matrix by the sum of each node’s row. This ensures that the aggregation is normalized based on the degrees of the nodes involved. The normalized aggregation is then passed through a linear layer to update the node embeddings.</p><p>Generalized Neighborhood Aggregation:</p><ul><li>The AGGREGATE operator in GNN models can be improved and generalized.</li><li>Neighborhood normalization can be used to address instability and sensitivity to node degrees.</li><li>Set pooling, based on permutation invariant neural networks, can provide a more sophisticated aggregation function.</li><li>Janossy pooling, using permutation-sensitive functions, is another alternative approach that is more powerful than simple sum or mean aggregations.</li><li>Neighborhood attention, assigning attention weights to neighbors, is a popular strategy to improve the aggregation layer in GNNs.</li><li>GNN models with multi-headed attention are closely related to the transformer architecture used in NLP and computer vision.</li></ul><p>Over-smoothing and Neighborhood Influence:</p><ul><li>Over-smoothing is a common issue in GNNs where node representations become too similar after multiple iterations, limiting the ability to capture longer-term dependencies.</li><li>The influence of a node’s input feature on the final embeddings of other nodes can be quantified using the Jacobian matrix.</li><li>The influence of a node in a GNN with self-loop update or basic GNN models decreases as the number of layers increases, leading to over-smoothing.</li><li>Deeper models can hurt performance as they lose information about local neighborhood structures and embeddings become over-smoothed.</li></ul><p>Over-smoothing and neighborhood influence in GNNs can arise in various scenarios. Here are a couple of examples:</p><p>Social Network Analysis:</p><ul><li>Scenario: Analyzing a social network to predict user preferences or behaviors.</li><li>Over-smoothing: As the GNN iteratively aggregates information from the social connections of each user, the representations of different users become increasingly similar, losing individuality and specific characteristics.</li><li>Neighborhood Influence: The influence of a user’s initial features (e.g., demographics, interests) on the final representations of other users decreases as the number of GNN layers increases. This means that local neighborhood information becomes less influential, and the learned embeddings become more influenced by the overall structure of the social network.</li></ul><p>Recommendation Systems:</p><ul><li>Scenario: Building a recommendation system based on user-item interaction data.</li><li>Over-smoothing: After multiple iterations of GNN message passing, the representations of items become highly similar, leading to difficulties in distinguishing between different items. The recommendations may become generic and fail to capture the nuances and specific characteristics of individual items.</li><li>Neighborhood Influence: The influence of an item’s initial features (e.g., genre, popularity) on the final representations of other items diminishes with increasing GNN layers. This means that the contributions of nearby items in the user-item interaction graph become less significant, potentially leading to inadequate recommendations for niche or less-connected items.</li></ul><p>In both scenarios, over-smoothing and neighborhood influence can limit the expressive power of the GNN model, making it challenging to capture fine-grained details, individual differences, and local relationships within the graph data.</p><h4>Supplementary points</h4><ul><li>Over-smoothing is a common issue in GNNs where node-specific information is lost after multiple iterations of message passing.</li><li>Concatenation and skip-connections are techniques used to alleviate over-smoothing by preserving information from previous rounds of message passing during the update step.</li></ul><pre>import torch<br>import torch.nn as nn</pre><pre>class GNN(nn.Module):<br>    def __init__(self, input_dim, hidden_dim, output_dim):<br>        super(GNN, self).__init__()<br>        self.linear1 = nn.Linear(input_dim, hidden_dim)<br>        self.linear2 = nn.Linear(hidden_dim + input_dim, output_dim)</pre><pre>    def forward(self, h, adj_matrix):<br>        # Initial node representations<br>        h0 = self.linear1(h)<br>        <br>        # Message passing with concatenation-based skip connections<br>        m = torch.matmul(adj_matrix, h)<br>        h1 = self.linear2(torch.cat((h, m), dim=1))<br>        h_updated = h1 + h0  # Skip connection: add previous layer representation<br>        <br>        return h_updated</pre><p>In this example, we define a GNN module with two linear layers: linear1 and linear2. In the forward method, we perform the initial linear transformation (linear1) on the input node features h to obtain h0.</p><ul><li>During message passing, we calculate the aggregated messages m by multiplying the adjacency matrix adj_matrix with the current node representations h. Then, we concatenate the current node representations h with the aggregated messages m along the feature dimension using torch.cat. The concatenated tensor is passed through the second linear layer (linear2) to obtain h1.</li><li>To preserve information from the previous round of message passing, we add a skip connection by adding the initial node representations h0 to the updated node representations h1. This helps in alleviating over-smoothing and ensures that information from previous rounds is retained.</li><li>Concatenation-based skip connections involve concatenating the output of the base update function with the node’s previous-layer representation.</li><li>Linear interpolation skip connections interpolate between the previous representation and the updated representation based on neighborhood information.</li><li>These skip-connection methods help disentangle information and improve the numerical stability of optimization, allowing for deeper GNN models.</li><li>Gated updates apply techniques from recurrent neural networks (RNNs) to update the hidden state of each node based on observations from the neighbors.</li><li>Gated updates are effective at preventing over-smoothing and facilitating deep GNN architectures, particularly in tasks requiring complex reasoning over the graph structure.</li><li>Jumping knowledge connections leverage representations at each layer of message passing, allowing for improved final node representations by combining representations from multiple layers.</li><li>Adding jumping knowledge connections is a useful strategy to employ, where node embeddings from each layer are concatenated or combined using different functions to enhance performance across various tasks.</li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e77ffee523cc" width="1" height="1" alt=""><hr><p><a href="https://medium.com/the-modern-scientist/graph-neural-networks-series-part-4-the-gnns-message-passing-over-smoothing-e77ffee523cc">Graph Neural Networks Series | Part 4 |The GNNs, Message Passing &amp; Over-smoothing</a> was originally published in <a href="https://medium.com/the-modern-scientist">The Modern Scientist</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[I Stand Behind AI Art]]></title>
            <link>https://omar-hussein.medium.com/i-stand-behind-ai-art-ea3cfb36832f?source=rss-8fb24e986396------2</link>
            <guid isPermaLink="false">https://medium.com/p/ea3cfb36832f</guid>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[ai-art-generator]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[ai-art]]></category>
            <category><![CDATA[art]]></category>
            <dc:creator><![CDATA[Omar Hussein]]></dc:creator>
            <pubDate>Tue, 27 Dec 2022 22:27:07 GMT</pubDate>
            <atom:updated>2022-12-27T22:27:07.167Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*TgTw9YEwXg_eqdxq.png" /></figure><h4>Why I Believe in the Power and Potential of Artificial Intelligence in Art and Design</h4><p>Artificial intelligence (AI) has made incredible strides in recent years, and it has revolutionized many industries, including art and design. With the advent of AI art tools and platforms, anyone can now create professional-quality images and designs using only a computer and an internet connection.</p><p>As an artist and designer, I stand behind AI art and believe in its power and potential. I have seen firsthand how AI art tools and platforms can inspire and enhance the creative process, and how they can help artists and designers bring their ideas to life in ways that were previously unimaginable.</p><p>One of the main reasons I stand behind AI art is because it is an incredible democratization of art and design tools. No longer do you need expensive equipment or specialized training to create professional-quality images. With AI art tools, anyone can create stunning and impressive images based on their text prompts.</p><p>Another reason I stand behind AI art is because it allows for a level of creativity and self-expression that is unparalleled. With AI art tools, you can experiment with different styles, themes, and techniques in a way that is fast and easy. You can create and explore your artistic vision in a way that is simply not possible with traditional art and design tools.</p><p>I also stand behind AI art because it is a testament to the incredible progress that has been made in technology and artificial intelligence. The fact that we can now use AI to create art and design is a testament to the power and potential of this technology, and it is an exciting time to be a creator and artist.</p><p>In addition to the democratization of art and design tools and the potential for creativity and self-expression, there are several other compelling reasons why I stand behind AI art and believe in its potential.</p><p>One of these reasons is the cost efficiency of AI art tools and platforms. For small businesses, startups, and independent artists, the cost of traditional art and design tools can be a major barrier to entry. But with AI art tools, you can create professional-quality images and designs at a fraction of the cost of traditional tools. This makes AI art a cost-effective solution for small businesses and independent artists looking to create professional-quality images and designs on a budget.</p><p>Another reason I stand behind AI art is its potential to help bridge the gap between developed and developing countries. In many parts of the world, access to art and design tools is limited due to financial constraints or other barriers. But with AI art tools, anyone with an internet connection can create professional-quality images and designs, regardless of their location or financial resources. This has the potential to democratize art and design in a way that could help level the playing field and give more people around the world the opportunity to create and share their artistic vision.</p><h4>Is it Evil ?</h4><p>There are some artists who argue that AI art is bad and that it threatens the authenticity and originality of traditional art. However, I strongly disagree with this argument and believe that AI art has the potential to enhance, rather than destroy, the art world.</p><p>First and foremost, it is important to recognize that AI art is simply a tool, and like any tool, it can be used in a variety of ways. Some artists may choose to use AI art tools to augment their traditional art practices, while others may choose to use AI art tools as the primary medium for their creations. Both approaches are valid and can lead to the creation of beautiful and meaningful art.</p><p>Furthermore, AI art does not necessarily replace traditional art practices, but rather it offers a new and exciting medium for artistic expression. Just as traditional art has coexisted with other art forms throughout history, AI art can coexist with traditional art and offer new and innovative ways for artists to express themselves.</p><p>It is also important to recognize that AI art is still in its early stages and is constantly evolving. As AI technology improves and advances, so too will the capabilities and potential of AI art. This means that the future of AI art is still largely uncharted and has the potential to bring about new and exciting developments in the art world.</p><p>One argument in support of AI art is that it is simply following in the footsteps of traditional art practices. Throughout history, artists have always learned and been inspired by the art of those who came before them. In fact, it is this process of learning from and building upon the art of the past that has helped to advance and evolve the art world.</p><p>For example, the rise of oil painting in the 15th and 16th centuries was not a fluke, but rather the result of artists learning from and building upon the art of the past. These artists studied the techniques and styles of their predecessors, and then used this knowledge to create new and innovative works of art. In this way, the art world has always been a dynamic and constantly evolving field, with artists building upon the work of those who came before them.</p><p>AI art is simply following this same pattern, with AI algorithms learning from and building upon the art of the past in order to create new and innovative works of art. By looking at the art of the past and learning from it, AI algorithms are able to create new and exciting images that are based on the techniques and styles of the past, but also bring something new and unique to the table.</p><p>In conclusion, the argument that AI art is bad because it is learning from the art of the past is flawed. Throughout history, artists have always learned from and been inspired by the art of those who came before them, and this process has helped to advance and evolve the art world. AI art is simply following this same pattern, and it has the potential to bring about new and exciting developments in the</p><h4>AI &amp; Jobs</h4><p>It is understandable that the rise of AI art may raise concerns among some artists about the potential loss of jobs or income. However, it is important to recognize that the adoption of new technologies has always had the potential to disrupt traditional industries and job markets. However, history has shown that these disruptions can also lead to new and exciting opportunities for those who are able to adapt and embrace change.</p><p>One way to address the potential loss of jobs or income due to the rise of AI art is to focus on educating and training artists in the use of AI art tools and techniques. This can help to ensure that artists are able to adapt to the changing landscape of the art world and continue to create and share their work.</p><p>It is also important to recognize that AI art is still in its early stages, and it is not yet clear what the full impact of AI art on the art world will be. It is possible that AI art may lead to new and exciting opportunities for artists, rather than taking away from existing opportunities.</p><p>The development of the computer is a good example of this. When computers first emerged, many people were worried that they would lead to the automation of a wide range of jobs, potentially putting many people out of work.</p><p>However, it is important to recognize that the adoption of new technologies can also lead to the creation of new and exciting job opportunities. For example, the development of computers has led to the creation of many new jobs in the tech industry, such as software developers, network administrators, and IT support specialists. In this way, new technologies can create new opportunities for people to use their skills and expertise to contribute to the economy and society.</p><p>It is also worth noting that the impact of new technologies on the job market is often complex and multifaceted. While some jobs may be automated or made obsolete by new technologies, other jobs may be created or enhanced. In the end, the net impact of new technologies on the job market is difficult to predict and can vary depending on a variety of factors.</p><p>Overall, while it is understandable to have concerns about the potential loss of jobs or income due to the rise of new technologies, it is also important to recognize that these technologies can also bring about new and exciting opportunities.</p><h4>Rights ?</h4><p>I believe that what is done is done. Now it is a question of what now ?</p><p>It is important to educate artists, developers, and other stakeholders about the importance of obtaining consent for the use of data. This may involve providing training or resources that help people understand the ethical and legal considerations involved in the use of data.</p><p>In addition to these steps, it may also be helpful to work with industry groups and other organizations to develop best practices and standards for the use of data in AI art and other applications. By collaborating with others in the field, it may be possible to develop more comprehensive and effective approaches to addressing the issue of consent.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ea3cfb36832f" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Python data classes]]></title>
            <link>https://medium.com/the-modern-scientist/python-data-classes-d565147dc824?source=rss-8fb24e986396------2</link>
            <guid isPermaLink="false">https://medium.com/p/d565147dc824</guid>
            <dc:creator><![CDATA[Omar Hussein]]></dc:creator>
            <pubDate>Tue, 20 Dec 2022 04:57:08 GMT</pubDate>
            <atom:updated>2023-01-09T13:49:25.284Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*OgdI5TVdWPv6MaxcdSNvzA.png" /><figcaption>Photo by Artturi Jalli on Unsplash</figcaption></figure><p>Python&#39;s dataclass module, introduced in Python 3.7, is a decorator that helps you create classes that are primarily used to store data. These classes are similar to namedtuple classes, but they have several additional features, such as default values, type hints, and methods.</p><p>To use the dataclass decorator, you first need to import it from the dataclasses module. Then, you can use it to decorate your class definition. For example, here is a simple Person class with a name and age field:</p><pre>from dataclasses import dataclass<br><br>@dataclass<br>class Person:<br>    name: str<br>    age: int<br><br></pre><p>The dataclass decorator automatically generates several useful methods for you, such as __init__, __repr__, and __eq__.</p><p>To create an instance of the Person class, you can use the standard syntax:</p><p>You can also specify default values for your fields by using the default argument:</p><p>In addition to the default methods, you can also define your own methods within the dataclass. For example, you might want to define a grow_older method that increments the person’s age:</p><p>You can also specify type hints for your fields using the typing module. This can be helpful for documentation and static analysis tools. For example:</p><pre>from typing import List<br><br>@dataclass<br>class Person:<br>    name: str<br>    age: int = 18<br>    friends: List[str] = []<br>    <br>    def add_friend(self, friend_name: str):<br>        self.friends.append(friend_name)<br><br></pre><p>There are several other options and decorators available in the dataclasses module, such as the field decorator, which allows you to specify additional options for individual fields. For more information, you can refer to the official Python documentation for the dataclasses module.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d565147dc824" width="1" height="1" alt=""><hr><p><a href="https://medium.com/the-modern-scientist/python-data-classes-d565147dc824">Python data classes</a> was originally published in <a href="https://medium.com/the-modern-scientist">The Modern Scientist</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Hot Keys for Programming from OS to IDEs.]]></title>
            <link>https://medium.com/the-modern-scientist/hot-keys-for-programming-from-os-to-ides-1116c6ff0b26?source=rss-8fb24e986396------2</link>
            <guid isPermaLink="false">https://medium.com/p/1116c6ff0b26</guid>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[sublimetext]]></category>
            <category><![CDATA[pycharm]]></category>
            <category><![CDATA[vscode]]></category>
            <category><![CDATA[atom]]></category>
            <dc:creator><![CDATA[Omar Hussein]]></dc:creator>
            <pubDate>Sun, 18 Dec 2022 19:42:34 GMT</pubDate>
            <atom:updated>2023-01-09T14:04:50.702Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*s0Do1s2GWRqZlAC3" /><figcaption>Photo by <a href="https://unsplash.com/@glenncarstenspeters?utm_source=medium&amp;utm_medium=referral">Glenn Carstens-Peters</a> on <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Here are some examples of hotkeys that are commonly used for programming on Mac, Windows, and Linux:</p><p>Mac:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*8nu7tC--leQ8b5h3" /><figcaption>Photo by <a href="https://unsplash.com/fr/@patrikward?utm_source=medium&amp;utm_medium=referral">Patrick Ward</a> on <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><ul><li>Command + Shift + O: Open a file</li><li>Command + S: Save a file</li><li>Command + Shift + S: Save a file as</li><li>Command + Shift + F: Find and replace text</li><li>Command + /: Comment out a line of code</li><li>Command + Shift + /: Comment out a block of code</li></ul><p>Windows:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*Bqw2GyqQsVJR1Mlf" /><figcaption>Photo by <a href="https://unsplash.com/@windows?utm_source=medium&amp;utm_medium=referral">Windows</a> on <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><ul><li>Ctrl + O: Open a file</li><li>Ctrl + S: Save a file</li><li>Ctrl + Shift + S: Save a file as</li><li>Ctrl + F: Find and replace text</li><li>Ctrl + /: Comment out a line of code</li><li>Ctrl + Shift + /: Comment out a block of code</li></ul><p>Linux:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*EYjyDloSauNpj8Ff" /><figcaption>Photo by <a href="https://unsplash.com/@6heinz3r?utm_source=medium&amp;utm_medium=referral">Gabriel Heinzer</a> on <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><ul><li>Ctrl + O: Open a file</li><li>Ctrl + S: Save a file</li><li>Ctrl + Shift + S: Save a file as</li><li>Ctrl + F: Find and replace text</li><li>Ctrl + /: Comment out a line of code</li><li>Ctrl + Shift + /: Comment out a block of code</li></ul><p>Note that these hotkeys may vary depending on the specific text editor or programming environment you are using.</p><h4>VS Code</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*ff1gIEk3dkCIahZy" /><figcaption>Photo by <a href="https://unsplash.com/@clemhlrdt?utm_source=medium&amp;utm_medium=referral">Clément Hélardot</a> on <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Here are some examples of hotkeys that are commonly used for programming on Mac, Windows, and Linux:</p><p>Mac:</p><ul><li>Command + Shift + O: Open a file</li><li>Command + S: Save a file</li><li>Command + Shift + S: Save a file as</li><li>Command + Shift + F: Find and replace text</li><li>Command + /: Comment out a line of code</li><li>Command + Shift + /: Comment out a block of code</li></ul><p>Windows:</p><ul><li>Ctrl + O: Open a file</li><li>Ctrl + S: Save a file</li><li>Ctrl + Shift + S: Save a file as</li><li>Ctrl + F: Find and replace text</li><li>Ctrl + /: Comment out a line of code</li><li>Ctrl + Shift + /: Comment out a block of code</li></ul><p>Linux:</p><ul><li>Ctrl + O: Open a file</li><li>Ctrl + S: Save a file</li><li>Ctrl + Shift + S: Save a file as</li><li>Ctrl + F: Find and replace text</li><li>Ctrl + /: Comment out a line of code</li><li>Ctrl + Shift + /: Comment out a block of code</li></ul><p>Note that these hotkeys may vary depending on the specific text editor or programming environment you are using.</p><h4>Sublime Text</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*2eYfOQ0gGqVtlyr3" /><figcaption>Photo by <a href="https://unsplash.com/@maxchen2k?utm_source=medium&amp;utm_medium=referral">Max Chen</a> on <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><ul><li>Ctrl + Shift + P: Open the command palette</li><li>Ctrl + P: Quickly open a file</li><li>Ctrl + N: Open a new file</li><li>Ctrl + S: Save the current file</li><li>Ctrl + Shift + S: Save the current file with a different name</li><li>Ctrl + F: Find and replace text</li><li>Ctrl + H: Replace text</li><li>Ctrl + /: Comment out a line of code</li><li>Ctrl + Shift + /: Comment out a block of code</li><li>Ctrl + Shift + [ or ]: Indent or un-indent a block of code</li><li>Ctrl + K, Ctrl + F: Format the current file</li><li>F5: Run the current file</li><li>Ctrl + B: Build the project</li><li>Ctrl + Shift + B: Run the build system</li></ul><h4>Atom</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*DG3SBgjrb4elKVRm" /><figcaption>Photo by <a href="https://unsplash.com/@vlisidis?utm_source=medium&amp;utm_medium=referral">Terry Vlisidis</a> on <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><ul><li>Ctrl + Shift + P: Open the command palette</li><li>Ctrl + P: Quickly open a file</li><li>Ctrl + N: Open a new file</li><li>Ctrl + S: Save the current file</li><li>Ctrl + Shift + S: Save the current file with a different name</li><li>Ctrl + F: Find and replace text</li><li>Ctrl + H: Replace text</li><li>Ctrl + /: Comment out a line of code</li><li>Ctrl + Shift + /: Comment out a block of code</li><li>Ctrl + Shift + [ or ]: Indent or un-indent a block of code</li><li>Ctrl + K, Ctrl + F: Format the current file</li><li>F5: Run the current file</li><li>Ctrl + B: Build the project</li><li>Ctrl + Shift + B: Run the build system</li></ul><h4>PyCharm</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*BwoWkW9KWpoDfC3i" /><figcaption>Photo by <a href="https://unsplash.com/@sapegin?utm_source=medium&amp;utm_medium=referral">Artem Sapegin</a> on <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><ul><li>Ctrl + Shift + A: Find any action</li><li>Ctrl + N: Open a class</li><li>Ctrl + Shift + N: Open a file</li><li>Ctrl + S: Save the current file</li><li>Ctrl + Shift + S: Save the current file with a different name</li><li>Ctrl + F: Find and replace text</li><li>Ctrl + R: Replace text</li><li>Ctrl + /: Comment out a line of code</li><li>Ctrl + Shift + /: Comment out a block of code</li><li>Tab: Indent a block of code</li><li>Shift + Tab: Un-indent a block of code</li><li>Ctrl + Alt + L: Reformat code</li><li>Ctrl + Shift + F10: Run the current file</li><li>Ctrl + Shift + F9: Debug the current file</li></ul><h4>IntelliJ</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*uD-je3M4cxfzM7oG" /><figcaption>Photo by <a href="https://unsplash.com/@cdr6934?utm_source=medium&amp;utm_medium=referral">Chris Ried</a> on <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><ul><li>Ctrl + Shift + A: Find any action</li><li>Ctrl + N: Open a class</li><li>Ctrl + Shift + N: Open a file</li><li>Ctrl + S: Save the current file</li><li>Ctrl + Shift + S: Save the current file with a different name</li><li>Ctrl + F: Find and replace text</li><li>Ctrl + R: Replace text</li><li>Ctrl + /: Comment out a line of code</li><li>Ctrl + Shift + /: Comment out a block of code</li><li>Tab: Indent a block of code</li><li>Shift + Tab: Un-indent a block of code</li><li>Ctrl + Alt + L: Reformat code</li><li>Ctrl + Shift + F10: Run the current file</li><li>Ctrl + Shift + F9: Debug the current file</li></ul><p>Note that these hotkeys may vary depending on the specific version of the text editor or integrated development environment (IDE) you are using and any custom key bindings.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1116c6ff0b26" width="1" height="1" alt=""><hr><p><a href="https://medium.com/the-modern-scientist/hot-keys-for-programming-from-os-to-ides-1116c6ff0b26">Hot Keys for Programming from OS to IDEs.</a> was originally published in <a href="https://medium.com/the-modern-scientist">The Modern Scientist</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Graph Neural Networks Series | Part 3 | Node embedding]]></title>
            <link>https://medium.com/the-modern-scientist/graph-neural-networks-series-part-3-node-embedding-36613cc967d5?source=rss-8fb24e986396------2</link>
            <guid isPermaLink="false">https://medium.com/p/36613cc967d5</guid>
            <category><![CDATA[graph]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[deep-learning]]></category>
            <category><![CDATA[gnn]]></category>
            <category><![CDATA[graph-neural-networks]]></category>
            <dc:creator><![CDATA[Omar Hussein]]></dc:creator>
            <pubDate>Mon, 28 Nov 2022 17:21:34 GMT</pubDate>
            <atom:updated>2023-05-16T14:40:54.998Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*6HxH9M3ccAG1u9FV" /><figcaption>Photo by <a href="https://unsplash.com/@othentikisra?utm_source=medium&amp;utm_medium=referral">israel palacio</a> on <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><h3>Introduction</h3><p>Graph neural networks (GNNs) are a type of neural network that can operate on graphs. A GNN can be used to learn a representation of the nodes in a graph, known as a node embedding. <strong><em>Node embeddings</em></strong> can be used for a variety of tasks, such as link prediction and node classification.</p><h3>What is an Embedding ?</h3><p>An embedding is a mapping from an input space to a lower-dimensional space. For example, we can map a 3D vector to a 2D vector. We can think of an embedding as a function that takes an input (such as a node in a graph) and outputs a vector. Node embeddings are a type of embedding that maps nodes in a graph to vectors. Node embeddings can be used for a variety of tasks, such as link prediction and node classification.</p><h3>Why Use Node Embeddings ?</h3><p>Node embeddings have a number of advantages over other methods for learning node representations. First, node embeddings can be trained using standard neural network optimization methods. Second, node embeddings can be trained on graphs with arbitrary structure. Finally, node embeddings can be used to represent nodes in multiple graphs simultaneously.</p><h3>How are Node Embeddings Learned ?</h3><p>Node embeddings are learned by training a neural network to map nodes in a graph to low-dimensional vectors. The network is trained using a loss function that encourages the vectors for similar nodes to be close together and the vectors for dissimilar nodes to be far apart.</p><p><strong>To explain this a bit more</strong>,<br>When we say that node embeddings are learned, it means that we train a neural network to generate low-dimensional vector representations (embeddings) for each node in a graph. The process of learning node embeddings involves training the neural network using a specific objective or loss function.</p><p>The neural network takes the graph as input and applies a series of operations to transform the node features and capture the relationships between nodes. These operations can include graph convolutional layers, pooling layers, or attention mechanisms, depending on the specific architecture of the neural network.</p><p>During training, the network aims to optimize its parameters to produce embeddings that capture meaningful information about the nodes in the graph. This optimization is achieved by minimizing a loss function that quantifies the dissimilarity between the predicted node embeddings and the desired target embeddings.</p><p>The loss function encourages the network to make embeddings of similar nodes closer together in the embedding space, while pushing embeddings of dissimilar nodes farther apart. This process helps the network to learn representations that capture the underlying structure, characteristics, and relationships within the graph.</p><p>By iteratively updating the network’s parameters and minimizing the loss, the neural network gradually learns to generate node embeddings that encode useful information about the nodes in the graph. These embeddings can then be used for various downstream tasks, such as link prediction, node classification, or visualization.</p><p>In summary, learning node embeddings involves training a neural network to map nodes in a graph to low-dimensional vectors through an optimization process that encourages similar nodes to have close embeddings and dissimilar nodes to have distant embeddings.</p><h4>Example of the process:</h4><p>It is okay if you don’t fully understand the example here, that’s okay, I just want you to get a bird’s eye view of the process.</p><p>Let’s consider a transportation network consisting of several cities connected by roads. We want to learn node embeddings for each city in the network using a graph neural network (GNN) approach. Here’s how the process might unfold:</p><ol><li>Data Preparation: We collect data about the transportation network, including information about cities, their geographical coordinates, and the roads connecting them. This data forms the basis of our graph representation.</li><li>Graph Construction: We represent the transportation network as a graph, where each city is a node and the roads between cities are edges. The graph structure captures the connections and relationships between cities.</li><li>Node Features: We can enrich the graph by including additional features for each city, such as population size, transportation infrastructure, or economic indicators. These features provide context and additional information about the cities.</li><li><strong>Node Embedding Learning</strong>: We train a graph neural network on this transportation graph to learn node embeddings. The GNN takes the graph structure and node features as input and applies neural network operations to capture the relationships and patterns in the data.</li><li>Loss Function: During training, we define a loss function that measures the dissimilarity between the predicted node embeddings and the desired target embeddings. The loss function encourages similar cities (e.g., cities with similar transportation characteristics or geographical proximity) to have embeddings that are close together in the embedding space.</li><li>Optimization: The GNN is optimized by iteratively updating its parameters to minimize the loss function. This optimization process fine-tunes the neural network to generate embeddings that capture relevant information about each city’s transportation characteristics.</li><li>Node Embedding Interpretation: Once the GNN is trained, we can interpret the learned node embeddings. The embeddings encode meaningful representations of the cities in a lower-dimensional space. We can analyze the embeddings to identify clusters of similar cities based on transportation characteristics, uncover central or influential cities based on their embedding proximity to others, or even use the embeddings as inputs for downstream tasks such as predicting traffic patterns or recommending optimal routes.</li></ol><p>By learning node embeddings in this transportation network, we can gain insights into the structural properties, relationships, and characteristics of the cities within the transportation system. The node embeddings capture important information about the cities’ transportation features, allowing us to perform various analyses and make informed decisions related to transportation planning, infrastructure improvements, or optimizing travel experiences for individuals within the network.</p><p>I want to go back and expand a bit on point <strong><em>number 4</em></strong> <strong><em>Node Embedding Learning </em></strong>because there is a lot more to talk about here. Here are some of the things you need to know.</p><p><strong><em>Node Embedding Learning</em></strong></p><p><strong>Input Representation</strong>: In our transportation network, the input to the GNN consists of the graph structure, represented by the adjacency matrix or edge lists, and node features such as population size, transportation infrastructure, or economic indicators. In PyTorch Geometric ( A famous python framework for graphs), the edge structure of the graph can be represented using the edge_index list. edge_index is a 2D tensor that contains the indices of the nodes connected by an edge. Each column of the edge_index tensor represents an edge in the graph. For example, if edge_index has shape (2, E), where E is the number of edges, the first row contains the indices of the source nodes and the second row contains the indices of the target nodes.</p><p>The edge_index list is a common way to represent the graph structure in many graph neural network libraries, including PyTorch Geometric. It provides a compact and efficient representation that allows the GNN to efficiently process the edge connections during the message passing and aggregation steps.</p><p>In addition to the edge structure, the GNN also requires node features to learn meaningful embeddings. These node features can include various attributes or properties of the nodes, such as population size, transportation infrastructure, or economic indicators. The node features are typically represented as a node feature matrix, where each row corresponds to a node and each column represents a specific feature. The node feature matrix is then combined with the edge structure (i.e., edge_index) to form the input representation for the GNN.</p><p>Here’s an example of representing a transportation network using the edge_index list and node features in PyTorch Geometric:</p><pre>import torch<br>from torch_geometric.data import Data<br><br># Define the adjacency matrix (edge connections)<br>edge_index = torch.tensor([[0, 1, 1, 2, 3, 3, 4, 4], [1, 0, 2, 1, 2, 4, 3, 5]], dtype=torch.long)<br><br># Define the node features<br>node_features = torch.tensor([[100, 5.0, 20], [200, 4.2, 15], [150, 4.8, 18], [180, 4.6, 25], [120, 4.9, 12], [250, 4.1, 30]], dtype=torch.float)<br><br># Create the PyTorch Geometric Data object<br>data = Data(x=node_features, edge_index=edge_index)<br><br># Print the data object<br>print(data)</pre><p>In this example, we have a transportation network with 6 nodes and 8 edges. The edge_index represents the connections between the nodes, where each column of the edge_index tensor corresponds to an edge. The node_features tensor contains the features of each node, such as population size, transportation infrastructure, and economic indicators.</p><p>By creating a Data object with the node_features and edge_index, we can easily pass this representation to a GNN model in PyTorch Geometric for further processing, such as learning node embeddings or performing downstream tasks on the transportation network data.</p><p><strong>Message Passing (We will revisit this point later)</strong>: The GNN propagates information across the graph through a series of message passing steps. At each step, information is aggregated from neighboring nodes and combined with the node’s own features to update its representation.</p><p><strong>Graph Convolutional Layers (We will revisit this point later)</strong>: The GNN typically consists of multiple graph convolutional layers, where each layer applies a graph-based operation to refine the node representations. These operations often involve aggregating information from neighboring nodes and applying non-linear transformations.</p><p><strong>Non-Linearity and Activation Functions</strong>: Non-linear activation functions such as ReLU (Rectified Linear Unit) or sigmoid are applied to introduce non-linearity and capture complex relationships between nodes and their features.</p><p><strong>Parameter Learning</strong>: During training, the GNN’s parameters (weights and biases) are adjusted through an optimization process. This process involves forward propagation to compute the predicted node embeddings, followed by backpropagation to calculate gradients and update the parameters using techniques like stochastic gradient descent (SGD) or Adam optimizer.</p><h3>An Encode-Decoder perspective</h3><p>This way of viewing graph representation learning is very common in how node embedding methods are represented. In the encoder-decoder framework, we view the process of learning node embeddings as learning a mapping from nodes in a graph to low-dimensional vectors, and then learning a mapping back from the vectors to the nodes. The encoder is a neural network that maps nodes in a graph to low-dimensional vectors. The decoder is a neural network that maps the vectors back to the nodes. The encoder and decoder are trained jointly so that the vectors produced by the encoder can be used by the decoder to reconstruct the original graph. The loss function used to train the encoder-decoder is based on the idea that similar nodes should have similar embeddings. This loss function encourages the embeddings for similar nodes to be close together and the embeddings for dissimilar nodes to be far apart.</p><p>The encoder-decoder framework can be used to learn node embeddings for any type of graph, including directed graphs, undirected graphs, and graphs with multiple types of nodes and edges. The encoder-decoder framework can be used to learn node embeddings for any type of graph, including directed graphs, undirected graphs, and graphs with multiple types of nodes and edges.</p><h4>The encoder</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*YOumbSaTTkXrZTSP" /><figcaption>Nodes in Graph get encoded to an embedded space.</figcaption></figure><p>The encoder plays a crucial role in learning meaningful representations of nodes in a graph. The encoder is a neural network component responsible for mapping nodes in the graph to low-dimensional vectors, often referred to as node embeddings. The encoder operates on the input graph data, which typically includes the graph structure (e.g., adjacency matrix or edge lists) and node features (e.g., attributes, properties, or contextual information associated with each node).</p><p>The encoder performs the following key tasks:</p><ol><li>Feature Extraction: The encoder analyzes the input graph data and extracts relevant features from each node. This can involve processing node attributes, incorporating neighborhood information, and considering the global graph structure. The goal is to capture informative characteristics of the nodes that are indicative of their roles, relationships, or importance within the graph.</li><li>Non-linear Transformation: The encoder applies non-linear transformations to the extracted features, allowing for complex relationships and patterns in the graph to be captured. This can be achieved through the use of activation functions, such as ReLU (Rectified Linear Unit), which introduce non-linearities into the encoding process.</li><li>Embedding Generation: The encoder combines the transformed features to generate low-dimensional vector representations, known as node embeddings. These embeddings aim to capture essential information about each node in a compact and meaningful way. The encoder learns to map nodes to embeddings in a manner that maximizes the representation’s ability to capture relevant graph properties, such as connectivity patterns or community structures.</li><li>Latent Space Encoding: The encoder projects the node embeddings into a latent space, where nodes with similar properties or behaviors are expected to be close together. The latent space representation enables efficient comparisons, clustering, and downstream tasks that rely on similarity measures or dimensionality reduction techniques.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*h2vzIeKRqR9u0FA3.png" /></figure><h4>The Decoder</h4><p>The role of the decoder is to reconstruct certain graph statistics from the node embedding that are generated by the encode. Given a Node embedding Z: Node embedding, the decoder needs to be able to decode this embedding back to the original node. In the case of link prediction, the decoder is a function that takes as input the node embedding z and outputs the set of neighbors OR it the graph adjacency matrix. While there are many ways of building decoders but the standard method to create a decoder is to define pairwise decoders. <strong>Pairwise decoders predict the relationship OR similarity between Pairs of nodes in the graph.</strong> So lets refer to this pairwise decoder just as decoder. The pair embeddings (Zu,Zv) are passed to the decoder which outputs the connection OR similarity between the two nodes. The goal is to optimize the encoder and decoder to minimize the reconstruction loss of that relationship. Formally define the decoder-encoder mathematical equation. dec(enc(u),enc(v)) = dec(Zu,Zv). This is approximately S[𝑢,𝑣] The above equation says that the decoder function should return the same result when it takes the encoder output of two nodes u and v as input or the node embedding of those two nodes as input.</p><h4>Optimizing an encode-decoder model</h4><p>The encode-decoder model can be optimized using different methods. The most common method is by using Reconstruction loss which is defined as how well the decode can predict the similarity between two nodes in the graph. In this case, the reconstruction loss L = S[𝑢,𝑣] — dec(enc(u),enc(v)) , Where 𝑆[𝑢,𝑣] ~ A[𝑢,𝑣]. The above equation says that the reconstruction loss is the difference between the actual similarity between the two nodes and the predicted similarity. To optimize the model, the goal is to minimize the reconstruction loss. `Whatever that similarity is` This depends on how you define the decoder. the loss function might be a mean-squared error or a classification loss such as cross entropy.</p><h4>Factorization-Based approaches in Graphs.</h4><p>Factorization-based methods for learning node representations are based on the idea of factorizing a matrix that represents the relationships between nodes in a graph. For example, given a graph with 𝑁 nodes and 𝑀 edges, we can represent the relationships between the nodes using an 𝑁×𝑁 adjacency matrix 𝐴. Factorizing the adjacency matrix 𝐴 results in two 𝑁×𝐾 matrices 𝑊 and 𝑉, where 𝐾 is the number of dimensions of the node embeddings. The matrix 𝑊 represents the node embeddings and the matrix 𝑉 represents the edge embeddings. Factorizing the adjacency matrix 𝐴 results in two 𝑁×𝐾 matrices 𝑊 and 𝑉, where 𝐾 is the number of dimensions of the node embeddings. The matrix 𝑊 represents the node embeddings and the matrix 𝑉 represents the edge embeddings. There are a number of different ways to factorize the adjacency matrix 𝐴, including singular value decomposition (SVD) and non-negative matrix factorization (NMF).</p><h4>Random walks embedding</h4><p>Random walk-based methods for learning node representations are based on the idea of randomly walking through the graph and using the sequence of nodes visited by the random walk to learn a node embedding. For example, given a graph with 𝑁 nodes and 𝑀 edges, we can represent the relationships between the nodes using an 𝑁×𝑁 adjacency matrix 𝐴. We can then use the adjacency matrix to define a transition matrix 𝑇, which is a 𝑁×𝑁 matrix that defines the probabilities of transitioning from one node to another. We can then use the transition matrix to generate a random walk through the graph. The random walk can be used to learn a node embedding by representing each node in the graph as a vector. The elements of the vector are the probabilities of transitioning from the node to each of the other nodes in the graph.</p><p>Here is an <em>analogy</em> of random walks: Imagine you are in a city and you want to go to the grocery store. You could take a bus, which would take you from your current location to the grocery store. Or, you could walk. If you walk, you will likely take a different path each time, depending on the sidewalks, crosswalks, and other obstacles in your way. However, if you take the bus, you will likely take the same route each time. In this analogy, the city is the graph, the grocery store is the target node, the bus is the transition matrix, and the path you take is the random walk. There are a number of different ways to generate a random walk through a graph, including uniform sampling, Metropolis-Hastings sampling, and Gibbs sampling. But simply put the random walks purpose is to generate a sequence of nodes that can be used to learn a node embedding. The advantages of random walk-based methods over traditional methods for learning node representations is that they can be used to learn node embeddings for any type of graph, including directed graphs, undirected graphs, and graphs with multiple types of nodes and edges. In addition, random walk-based methods can be used to learn node embeddings for graphs with arbitrary structure.</p><h3>Link Prediction Example</h3><p>We use node embeddings here for link prediction.</p><pre>import os.path as osp<br><br>import torch<br>from sklearn.metrics import roc_auc_score<br><br>import torch_geometric.transforms as T<br>from torch_geometric.datasets import Planetoid<br>from torch_geometric.nn import GCNConv<br>from torch_geometric.utils import negative_sampling<br><br>device = torch.device(&#39;cuda&#39; if torch.cuda.is_available() else &#39;cpu&#39;)<br>transform = T.Compose([<br>    T.NormalizeFeatures(),<br>    T.ToDevice(device),<br>    T.RandomLinkSplit(num_val=0.05, num_test=0.1, is_undirected=True,<br>                      add_negative_train_samples=False),<br>])<br>path = osp.join(osp.dirname(osp.realpath(__file__)), &#39;..&#39;, &#39;data&#39;, &#39;Planetoid&#39;)<br>dataset = Planetoid(path, name=&#39;Cora&#39;, transform=transform)<br># After applying the `RandomLinkSplit` transform, the data is transformed from<br># a data object to a list of tuples (train_data, val_data, test_data), with<br># each element representing the corresponding split.<br>train_data, val_data, test_data = dataset[0]<br><br><br>class Net(torch.nn.Module):<br>    def __init__(self, in_channels, hidden_channels, out_channels):<br>        super().__init__()<br>        self.conv1 = GCNConv(in_channels, hidden_channels)<br>        self.conv2 = GCNConv(hidden_channels, out_channels)<br><br>    def encode(self, x, edge_index):<br>        x = self.conv1(x, edge_index).relu()<br>        return self.conv2(x, edge_index)<br><br>    def decode(self, z, edge_label_index):<br>        return (z[edge_label_index[0]] * z[edge_label_index[1]]).sum(dim=-1)<br><br>    def decode_all(self, z):<br>        prob_adj = z @ z.t()<br>        return (prob_adj &gt; 0).nonzero(as_tuple=False).t()<br><br><br>model = Net(dataset.num_features, 128, 64).to(device)<br>optimizer = torch.optim.Adam(params=model.parameters(), lr=0.01)<br>criterion = torch.nn.BCEWithLogitsLoss()<br><br><br>def train():<br>    model.train()<br>    optimizer.zero_grad()<br>    z = model.encode(train_data.x, train_data.edge_index)<br><br>    # We perform a new round of negative sampling for every training epoch:<br>    neg_edge_index = negative_sampling(<br>        edge_index=train_data.edge_index, num_nodes=train_data.num_nodes,<br>        num_neg_samples=train_data.edge_label_index.size(1), method=&#39;sparse&#39;)<br><br>    # Concat positive and negative edge indices.<br>    edge_label_index = torch.cat(<br>        [train_data.edge_label_index, neg_edge_index],<br>        dim=-1,<br>    )<br>    # Label for positive edges: 1, for negative edges: 0.<br>    edge_label = torch.cat([<br>        train_data.edge_label,<br>        train_data.edge_label.new_zeros(neg_edge_index.size(1))<br>    ], dim=0)<br><br>    # Note: The model is trained in a supervised manner using the given<br>    # `edge_label_index` and `edge_label` targets.<br>    out = model.decode(z, edge_label_index).view(-1)<br>    loss = criterion(out, edge_label)<br>    loss.backward()<br>    optimizer.step()<br>    return loss<br><br><br>@torch.no_grad()<br>def test(data):<br>    model.eval()<br>    z = model.encode(data.x, data.edge_index)<br>    out = model.decode(z, data.edge_label_index).view(-1).sigmoid()<br>    return roc_auc_score(data.edge_label.cpu().numpy(), out.cpu().numpy())<br><br># Train/Test Loop<br>best_val_auc = final_test_auc = 0<br>for epoch in range(1, 101):<br>    loss = train()<br>    val_auc = test(val_data)<br>    test_auc = test(test_data)<br>    if val_auc &gt; best_val_auc:<br>        best_val_auc = val_auc<br>        final_test_auc = test_auc<br>    print(f&#39;Epoch: {epoch:03d}, Loss: {loss:.4f}, Val: {val_auc:.4f}, &#39;<br>          f&#39;Test: {test_auc:.4f}&#39;)<br><br>print(f&#39;Final Test: {final_test_auc:.4f}&#39;)<br><br>z = model.encode(test_data.x, test_data.edge_index)<br>final_edge_index = model.decode_all(z)<br><br></pre><p>This code performs link prediction using a Graph Convolutional Network (GCN) on the Cora dataset. Here’s an explanation of the key parts of the code:</p><ol><li>Dataset Preparation:</li></ol><ul><li>The Planetoid dataset class from torch_geometric.datasets is used to load the Cora dataset.</li><li>The data is transformed using the RandomLinkSplit transform, which splits the graph into train, validation, and test sets and randomly adds negative samples for training.</li><li>The train, validation, and test data are extracted from the transformed dataset.</li></ul><p>2. Model Definition:</p><ul><li>The Net class defines the GCN model. It consists of two GCN layers (GCNConv), which are used for encoding the node features.</li><li>The encode method applies the GCN layers to the input features and edge indices to obtain node embeddings.</li><li>The decode method calculates the link scores between nodes using the node embeddings.</li><li>The decode_all method computes the adjacency matrix of the graph based on the node embeddings.</li></ul><p>3. Model Training:</p><ul><li>The train function performs a single training iteration. It sets the model to training mode, resets the optimizer gradients, encodes the node features using the GCN layers, performs negative sampling to generate negative examples, concatenates positive and negative edge indices, creates edge labels, calculates the link scores using the node embeddings, computes the loss using the BCEWithLogitsLoss criterion, performs backpropagation, and updates the model parameters.</li><li>The test function evaluates the model on the provided data. It sets the model to evaluation mode, encodes the node features, calculates the link scores, applies the sigmoid function to obtain probabilities, and computes the ROC AUC score.</li></ul><p>4. Train/Test Loop:</p><ul><li>The code runs a loop for a specified number of epochs (100 in this case).</li><li>In each epoch, it trains the model on the training data, evaluates it on the validation and test data, and keeps track of the best validation and final test ROC AUC scores.</li><li>The loss, validation ROC AUC, and test ROC AUC are printed for each epoch.</li></ul><h3>Shallow Embedding for Multi-Relational Data and Knowledge Graphs</h3><p>In the realm of graph-based machine learning, shallow embedding methods have proven to be effective in capturing the relationships between nodes. While we have previously explored shallow embedding (Node embeddings) for simple graphs. this section delves deeper into the exciting world of multi-relational graphs. We will explore the challenges associated with multi-relational data and knowledge graphs, discuss the concept of knowledge graph completion, and present various approaches to shallow embedding in this context.</p><h4>Understanding Multi-Relational Graphs and Knowledge Graph Completion</h4><p>Multi-relational graphs are complex networks consisting of nodes and edges, where each edge represents a specific relation between two nodes. These graphs are often referred to as knowledge graphs since they encode factual information as tuples of the form (u, τ, v), signifying a relation τ between nodes u and v. For instance, in a biomedical knowledge graph, an edge (u, TREATS, v) could indicate that the drug associated with node u treats the disease associated with node v. The primary task in knowledge graph completion is to predict missing edges or relations within the graph.</p><h4>Reconstructing Multi-Relational Data</h4><p>Similar to the case of simple graphs, embedding multi-relational graphs can be viewed as a reconstruction task. The goal is to learn low-dimensional embeddings for nodes that can accurately reconstruct the relationships between them. However, in multi-relational graphs, we face the additional challenge of dealing with diverse types of edges or relations.</p><p>To tackle this challenge, we enhance the decoder component of our model to make it multi-relational. Instead of solely considering pairs of node embeddings, the decoder now takes into account both node embeddings and relation types. This enables the model to predict the likelihood of the existence of an edge between two nodes, given their embeddings and the relation type. Various decoder functions have been proposed, including RESCAL, TransE, TransX, DistMult, ComplEx, and RotatE. Each decoder offers a unique approach to encoding and decoding relations between nodes.</p><h4>Loss Functions for Multi-Relational Node Embedding</h4><p>Choosing an appropriate loss function is crucial for training the model in multi-relational node embedding. One popular and efficient choice is the cross-entropy loss with negative sampling. This loss function compares the predicted probabilities of true and false edges in the graph. By sampling negative examples and employing logistic regression, the model can learn to differentiate between true and false relations more effectively.</p><p>Good resources</p><p><a href="https://towardsdatascience.com/pytorch-geometric-graph-embedding-da71d614c3a">PyTorch Geometric Graph Embedding</a></p><h3>References</h3><p>[1] Graph Representation learning, William</p><p>[2] Pytorch Geometric: <a href="https://github.com/pyg-team/pytorch_geometric/blob/master/examples/link_pred.py">pytorch_geometric/link_pred.py at master · pyg-team/pytorch_geometric · GitHub</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=36613cc967d5" width="1" height="1" alt=""><hr><p><a href="https://medium.com/the-modern-scientist/graph-neural-networks-series-part-3-node-embedding-36613cc967d5">Graph Neural Networks Series | Part 3 | Node embedding</a> was originally published in <a href="https://medium.com/the-modern-scientist">The Modern Scientist</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>