<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>posts // simo.sh</title><description>my rambles about tech</description><link>https://simo.sh</link><item><title>Migrating Raycast Clipboard History</title><link>https://simo.sh/notes/migrating-raycast-clipboard-history?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/notes/migrating-raycast-clipboard-history?utm_source=rss</guid><description>Moving my 1 year worth of clipboard history to a new machine</description><pubDate>Tue, 01 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I&apos;ve been a heavy &lt;a href=&quot;https://www.raycast.com/&quot;&gt;Raycast&lt;/a&gt; user for the last 3 years. One thing I especially love is the &lt;a href=&quot;https://www.raycast.com/core-features/clipboard-history&quot;&gt;Clipboard History&lt;/a&gt; feature - I&apos;ve been keeping the stuff I copy for the last 1 year. I can really easily look stuff up I copied ages ago - links, random JSON responses, images (which are OCR processed), memes within seconds.&lt;/p&gt;
&lt;p&gt;Recently I got a new &lt;a href=&quot;../blog/My-Gear.md#5ba2ac&quot;&gt;laptop&lt;/a&gt; and wanted to move over my history. Unfortunately, even with &lt;a href=&quot;https://manual.raycast.com/cloud-sync&quot;&gt;Raycast&apos;s Pro Plan&lt;/a&gt;, the clipboard history isn&apos;t synced as it&apos;s encrypted and stored on device. While researching how to do this I came across &lt;a href=&quot;https://www.reddit.com/r/raycastapp/comments/1bk2tyl/does_anyone_know_how_to_backup_the_clipboard/&quot;&gt;this&lt;/a&gt; Reddit post which suggests using an AppleScript to copy/paste several entries to a temporary file. I thought there should be another way - thankfully, &lt;a href=&quot;https://twitter.com/pertikaer&quot;&gt;@pertikaer&lt;/a&gt; helped me figure this out on Raycast&apos;s slack.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;On your new Mac, quit Raycast&lt;/li&gt;
&lt;li&gt;Move the &lt;code&gt;~/Library/Application Support/com.raycast.macos&lt;/code&gt; directory somewhere (to use as backup)&lt;/li&gt;
&lt;li&gt;Also backup the &lt;code&gt;database_key&lt;/code&gt; Keychain entry for &lt;code&gt;Raycast&lt;/code&gt; by copying it somewhere
&lt;img src=&quot;../../assets/Migrating-Raycast-Clipboard-History-20241001102149656.webp&quot; alt=&quot;Migrating Raycast Clipboard History-20241001102149656.webp&quot; /&gt;
&lt;img src=&quot;../../assets/Migrating-Raycast-Clipboard-History-20241001102308858.webp&quot; alt=&quot;Migrating Raycast Clipboard History-20241001102308858.webp&quot; /&gt;&lt;/li&gt;
&lt;li&gt;On the old Mac, AirDrop the &lt;code&gt;~/Library/Application Support/com.raycast.macos&lt;/code&gt; directory to the new one and move it to the same place.&lt;/li&gt;
&lt;li&gt;Get the &lt;code&gt;database_key&lt;/code&gt; from the old Mac&apos;s Keychain using the steps from before and overwrite it on the new Mac&apos;s keychain&lt;/li&gt;
&lt;li&gt;If you want to copy over your clipboard images as well, make sure to send &lt;code&gt;~/Library/Caches/com.raycast.macos&lt;/code&gt; to your other Mac as well.&lt;/li&gt;
&lt;li&gt;Start Raycast again&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>System Observability</title><link>https://simo.sh/blog/system-observability?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/blog/system-observability?utm_source=rss</guid><description>Simplifying my system observability with Grafana Alloy &amp; Grafana Cloud</description><pubDate>Fri, 30 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Intro&lt;/h1&gt;
&lt;p&gt;I&apos;ve been quite interested in system observability for the last few years. I&apos;ve played around with many different tools – &lt;a href=&quot;https://github.com/netdata/netdata&quot;&gt;NetData&lt;/a&gt;, &lt;a href=&quot;https://github.com/prometheus/prometheus&quot;&gt;Prometheus&lt;/a&gt;, &lt;a href=&quot;https://github.com/grafana/loki&quot;&gt;Loki&lt;/a&gt;, &lt;a href=&quot;https://github.com/grafana/grafana&quot;&gt;Grafana&lt;/a&gt; and several more.
A few months ago, I was scrolling through my RSS feed and saw an &lt;a href=&quot;https://grafana.com/blog/2024/04/09/grafana-alloy-opentelemetry-collector-with-prometheus-pipelines/&quot;&gt;article&lt;/a&gt; announcing Grafana&apos;s new project called Alloy – It&apos;s the successor to the Grafana Agent.
&lt;img src=&quot;../../assets/System-Observability-20240825190916414.webp&quot; alt=&quot;System Observability-20240825190916414.webp&quot; /&gt;
I&apos;d known about Grafana Agent for a while, but I never bothered trying it, I figured that now was the perfect time to try Alloy.&lt;/p&gt;
&lt;p&gt;This blog post/ramble will go over my preferred system observability setup.&lt;/p&gt;
&lt;p&gt;Before I go into detail about what Grafana Alloy is, I want to give some background about the current monitoring setup for my servers.&lt;/p&gt;
&lt;h1&gt;Background&lt;/h1&gt;
&lt;p&gt;I&apos;ve maintained a bunch of servers in different places – from &lt;a href=&quot;https://cloud.google.com/kubernetes-engine?hl=en&quot;&gt;Google Kubernetes Engine&lt;/a&gt; on GCP, &lt;a href=&quot;https://k3s.io/&quot;&gt;k3s&lt;/a&gt; on bare-metal, to plain old VPS boxes on &lt;a href=&quot;https://www.digitalocean.com/&quot;&gt;DigitalOcean&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I&apos;ll review my observability setup on my bare-metal home &quot;cluster&quot;. It&apos;s not really a cluster right now as it has only one node, but I&apos;ll eventually make a separate blog post talking more about it. In short, it&apos;s an Acer Nitro laptop running Debian 11 with k3s installed.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/System-Observability-20240826020850525.webp&quot; alt=&quot;System Observability-20240826020850525.webp&quot; /&gt;&lt;/p&gt;
&lt;p&gt;One of the most common ways to monitor a Kubernetes cluster is using the &lt;a href=&quot;https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack&quot;&gt;kube-prometheus-stack&lt;/a&gt; Helm chart.
It will deploy several components on your cluster (if you&apos;re familiar with how this stack works, feel free to skip the next section as it&apos;s a brief overview of it):&lt;/p&gt;
&lt;h2&gt;Prometheus&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/prometheus/prometheus&quot;&gt;Prometheus&lt;/a&gt; is a time series database and a monitoring system that collects metrics from different applications/exporters.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/Prometheus.svg&quot; alt=&quot;Prometheus.svg&quot; /&gt;
It uses a pull-based architecture where it will periodically scrape an endpoint on your services (typically called &lt;code&gt;/metrics&lt;/code&gt;) and store the resulting measurements in its internal database.&lt;/p&gt;
&lt;p&gt;If you have multiple nodes inside your cluster, you can have a single Prometheus instance with many targets, e.g. one &lt;code&gt;node_exporter&lt;/code&gt; instance per node. Each exporter instance can assign different labels to its metrics, such that you can filter your data when building your dashboards/alerts.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/Metric.svg&quot; alt=&quot;Metric.svg&quot; /&gt;
Exporters are separate services that you deploy that collect metrics and expose a Prometheus-compatible endpoint, such that Prometheus can scrape it. Some examples would be the official &lt;a href=&quot;https://github.com/prometheus/node_exporter&quot;&gt;node_exporter&lt;/a&gt;, the &lt;a href=&quot;https://github.com/prometheus-community/postgres_exporter&quot;&gt;postgres_exporter&lt;/a&gt;, and more.&lt;/p&gt;
&lt;h2&gt;Prometheus Operator&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/prometheus-operator/prometheus-operator&quot;&gt;Prometheus Operator&lt;/a&gt; is a project that deploys a Prometheus instance for you, while also giving you complete control over it using &lt;a href=&quot;https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/&quot;&gt;Custom Resources&lt;/a&gt; and tightly integrating with the Kubernetes API.
The operator utilizes service discovery to find targets to scrape automatically – this can be done using the  &lt;code&gt;ServiceMonitor&lt;/code&gt; custom resource:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: api-backend-service-monitor
  labels:
    release: kube-prometheus-stack
spec:
  selector:
    matchLabels:
      app: api-backend
  endpoints:
    - targetPort: 8080
      path: /metrics
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The operator will look for any &lt;code&gt;ServiceMonitor&lt;/code&gt; resources in your cluster and build the necessary config updates in Prometheus. You can also define your Alertmanager rules using the &lt;code&gt;PrometheusRule&lt;/code&gt; custom resource.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/grafana/mimir&quot;&gt;Grafana Mimir&lt;/a&gt; is a long-term storage engine for Prometheus metrics. Using Mimir allows you to &quot;split&quot; Prometheus&apos; storage from the collection/scraping process. This will come into play later when I talk about Grafana Cloud.&lt;/p&gt;
&lt;p&gt;Thankfully, you don&apos;t have to set all of this up manually as the kube-prometheus-stack helm chart &lt;em&gt;mostly&lt;/em&gt; handles all of this for you.&lt;/p&gt;
&lt;h2&gt;Grafana&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/grafana/grafana&quot;&gt;Grafana&lt;/a&gt; is a data visualization platform that allows you to build different dashboards based on a variety of data sources – Prometheus, Loki, PostgreSQL, etc.
&lt;img src=&quot;../../assets/System-Observability-20240826020950418.webp&quot; alt=&quot;System Observability-20240826020950418.webp&quot; /&gt;
Here&apos;s an example &lt;a href=&quot;https://grafana.com/grafana/dashboards/1860-node-exporter-full/&quot;&gt;dashboard&lt;/a&gt; that displays Prometheus metrics about the Linux Kubernetes node using the aforementioned &lt;code&gt;node_exporter&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Alertmanager&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/prometheus/alertmanager&quot;&gt;Alertmanager&lt;/a&gt; is pretty self-explanatory - you can build Prometheus alert rules that when triggered, will notify you via different receivers (e.g. Telegram / Slack / Discord).
An example rule for detecting high request latency would be:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;groups:
- name: HighLatencyAlerts
  rules:
  - alert: HighAverageRequestLatency
    expr: avg(rate(http_request_duration_seconds_sum{endpoint=&quot;/api/users&quot;}[5m])) / avg(rate(http_request_duration_seconds_count{endpoint=&quot;/api/users&quot;}[5m])) &amp;gt; 0.3
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: &quot;High average latency on /api/users endpoint&quot;
      description: &quot;Average request duration for /api/users endpoint is above 300ms for the last 5 minutes.&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These alerts can be very crucial in a production environment where you want to know what&apos;s happening in your system at all times.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/fr3fou/home/blob/95dba9d9129bce218ca395245c23f656804e8a11/apps/kube-prometheus-stack/kube-prometheus-stack.yaml&quot;&gt;Here&lt;/a&gt; is my (old) deployment of the kube-prometheus-stack.&lt;/p&gt;
&lt;p&gt;Unfortunately, this only handles a third of the &lt;a href=&quot;https://www.oreilly.com/library/view/distributed-systems-observability/9781492033431/ch04.html&quot;&gt;3 pillars of observability&lt;/a&gt;. We&apos;ll next introduce log aggregation to our system.&lt;/p&gt;
&lt;h2&gt;Loki&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/grafana/loki&quot;&gt;Loki&lt;/a&gt; is &quot;Like Prometheus, but for logs.&quot;. You can think of it as a database for your logs. Unlike Prometheus, it uses a Push-based architecture using its Promtail daemon.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/Loki.svg&quot; alt=&quot;Loki.svg&quot; /&gt;
Similarly to the different exporters, this can be deployed across multiple nodes and labeled with the necessary metadata.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/Log.svg&quot; alt=&quot;Log.svg&quot; /&gt;
Previously, I&apos;ve deployed Loki using the &lt;a href=&quot;https://github.com/grafana/helm-charts/tree/main/charts/loki-stack&quot;&gt;loki-stack&lt;/a&gt;. It&apos;s been &lt;em&gt;mostly&lt;/em&gt; fine except for one problem I encountered in a different production cluster while writing this blog post.&lt;/p&gt;
&lt;p&gt;Turns out my configuration for retention of 2 weeks hadn&apos;t been working ever since I deployed it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;	# ...
    table_manager:
      retention_deletes_enabled: true
      retention_period: 336h # 2 weeks
    # ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This meant that for the last 2 years, my Loki instance had been storing logs indefinitely, filling up my storage.
In this cluster, I&apos;d provisioned a 50GB SSD from GCP:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  persistence:
    enabled: true
    size: 50Gi
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It took almost 2 years for me to notice:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/System-Observability-20240828011756464.webp&quot; alt=&quot;1220&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This culminated in Loki being stuck in a &lt;code&gt;CrashLoopBackOff&lt;/code&gt;, unable to start for the last several days.
If I had alerts configured for this, I would&apos;ve found out much earlier, the irony :D&lt;/p&gt;
&lt;h1&gt;Other Solutions&lt;/h1&gt;
&lt;p&gt;I&apos;m always looking for new tools to play around with or introduce/replace in my stack. One thing that I&apos;ve been searching for is an all-in-one observability solution.
I&apos;d prefer if it was simple to set up, free, and flexible enough so that I can monitor anything while not being too heavy.
Some contenders that I&apos;ve been keeping an eye on include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://signoz.io/&quot;&gt;SigNoz&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://qryn.metrico.in/#/introduction&quot;&gt;qryn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/coroot/coroot&quot;&gt;Coroot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://baselime.io/&quot;&gt;Baselime&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://uptrace.dev/&quot;&gt;Uptrace&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/hyperdxio/hyperdx&quot;&gt;HyperDX&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I haven&apos;t had the time or opportunity to try them all, but will update this post with my experience with them if I ever do.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://grafana.com/blog/2024/04/09/grafana-alloy-opentelemetry-collector-with-prometheus-pipelines/&quot;&gt;announcement&lt;/a&gt; for Grafana Alloy made me want to experiment with it, as well as giving Grafana Cloud a chance.&lt;/p&gt;
&lt;p&gt;Grafana Alloy does not require Grafana Cloud but I decided to use it as it helped me simplify my observability deployment. You can still benefit from Grafana Alloy even if you self-host the rest of your observability infrastructure.&lt;/p&gt;
&lt;h1&gt;Grafana Cloud&lt;/h1&gt;
&lt;p&gt;When I saw Grafana Cloud a while ago I thought to myself &quot;Why would anyone want a cloud-managed Grafana instance? It&apos;s pretty simple to self-host&quot;. Turns out Grafana Cloud provides you with much more than just a Grafana instance – they give you a hosted version of Loki, Mimir, Alertmanager, k6 load testing, and more. Seeing &lt;em&gt;that&lt;/em&gt; piqued my interest.
Their free forever tier seemed more than enough for my use cases, I don&apos;t even have my billing information filled in. This post is not affiliated with Grafana in any way, I just like the product.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/System-Observability-20240828011702845.webp&quot; alt=&quot;System Observability-20240828011702845.webp&quot; /&gt;&lt;/p&gt;
&lt;h1&gt;Grafana Alloy&lt;/h1&gt;
&lt;p&gt;Grafana Alloy is an agent that runs on your servers – it&apos;s an OpenTelemetry Collector[^1]. Instead of having multiple different tools collect and store data, you can use as Alloy as a unified collector for everything.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/Alloy.svg&quot; alt=&quot;Alloy.svg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;It can act as a replacement for the scraper part of Prometheus, the agent part of Loki (Promtail), be a collector for traces, and more.&lt;/p&gt;
&lt;p&gt;It does &lt;em&gt;not&lt;/em&gt; store the actual data, however. It&apos;s merely a &quot;proxy&quot; that collects your telemetry and pushes it to some remote storage, e.g. Mimir (or a Prometheus instance directly) for metrics, Loki for logs, Tempo for traces, etc. It does, however, have ephemeral storage for replaying the WAL in case of disruption.&lt;/p&gt;
&lt;p&gt;This is where Grafana Cloud shined for me – I can get rid of the kube-prometheus-stack &amp;amp; loki-stack deployments, significantly reducing memory, storage, and maintenance overhead on my cluster(s).&lt;/p&gt;
&lt;h1&gt;Deployment&lt;/h1&gt;
&lt;p&gt;Alloy can be executed as a simple binary on your machine, as a system service, or be installed in a Kubernetes cluster using the official Helm charts.&lt;/p&gt;
&lt;p&gt;There are 2 Helm charts that deploy Grafana Alloy. One is the bare-bones &lt;a href=&quot;https://github.com/grafana/alloy/tree/main/operations/helm/charts/alloy&quot;&gt;Alloy&lt;/a&gt; chart which only deploys Alloy by itself with 0 configuration and the opinionated &lt;a href=&quot;https://github.com/grafana/k8s-monitoring-helm/tree/main/charts/k8s-monitoring&quot;&gt;k8s-monitoring&lt;/a&gt; chart. The latter deploys a few more services as well as builds the configuration for them out of the box.&lt;/p&gt;
&lt;h2&gt;Bare-bones Chart&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/fr3fou/home/commit/9850b7fc13d5e50e12efe8dddc1794587b79c194&quot;&gt;At first&lt;/a&gt; I played around with the bare-bones one. This meant that I had to write the configuration by hand. After messing around with it for a few hours and seeing the appeal of the opinionated one, I scrapped my config. Regardless, here&apos;s a quick overview of how it works.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://grafana.com/docs/alloy/latest/get-started/configuration-syntax/&quot;&gt;syntax&lt;/a&gt; for Grafana Alloy config files is inspired by &lt;a href=&quot;https://github.com/hashicorp/hcl&quot;&gt;HCL&lt;/a&gt;, If you&apos;ve ever written Terraform code, you basically know it.&lt;/p&gt;
&lt;p&gt;I was mainly interested in the official &lt;a href=&quot;https://grafana.com/solutions/kubernetes/&quot;&gt;Grafana Cloud Kubernetes integration&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/System-Observability-20240828012053073.webp&quot; alt=&quot;System Observability-20240828012053073.webp&quot; /&gt;
It comes with many different metrics, alerts, dashboards, etc. If you use Kubernetes, I highly recommend checking it out, I&apos;ll definitely keep using it for future projects. Part of the features are powered by Prometheus metrics and the rest by logs. This meant that I had to configure Alloy to collect both, so I started with &lt;a href=&quot;https://grafana.com/docs/alloy/latest/collect/logs-in-kubernetes/&quot;&gt;logging&lt;/a&gt; first.&lt;/p&gt;
&lt;p&gt;Alloy has 4 main components that do the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Discover&lt;/strong&gt; targets – &lt;code&gt;discovery.kubernetes&lt;/code&gt;, &lt;code&gt;discovery.ec2&lt;/code&gt;, &lt;code&gt;local.file_match&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Collect&lt;/strong&gt; data from targets – &lt;code&gt;prometheus.scrape&lt;/code&gt;, &lt;code&gt;loki.source.kubernetes&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Transform&lt;/strong&gt; data – &lt;code&gt;prometheus.relabel&lt;/code&gt;, &lt;code&gt;loki.relabel&lt;/code&gt;,  &lt;code&gt;loki.process&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Write&lt;/strong&gt; to a receiver – &lt;code&gt;loki.write&lt;/code&gt;, &lt;code&gt;prometheus.remote_write&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;// 1. Discover targets e.g. all Pods in your Kubernetes cluster
discovery.kubernetes &quot;pod&quot; {
  role = &quot;pod&quot;
}

// 2. Collect logs from our targets e.g. Pods in Kubernetes
loki.source.kubernetes &quot;pod_logs&quot; {
  targets    = discovery.relabel.pod_logs.output
  forward_to = [loki.process.pod_logs.receiver]
}

// 2. Collect logs from another source e.g Kubernetes Cluster Events
loki.source.kubernetes_events &quot;cluster_events&quot; {
  job_name   = &quot;integrations/kubernetes/eventhandler&quot;
  log_format = &quot;logfmt&quot;
  forward_to = [
    loki.process.kubernetes_logs.receiver,
  ]
}

// 3. Transform the log entries, e.g. adding static labels
loki.process &quot;kubernetes_logs&quot; {
  stage.static_labels {
      values = {
        cluster = &quot;anton&quot;,
        region = &quot;eu-east1&quot;
      }
  }

  forward_to = [loki.write.grafana_cloud.receiver]
}

// 4. Write our logs to the remote Loki instance
loki.write &quot;grafana_cloud&quot; {
  endpoint {
    url = &quot;https://logs-us-central1.grafana.net/loki/api/v1/push&quot;
    basic_auth {
      username = &quot;327384&quot;
      password = &quot;glc_eyJvI...&quot;
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Steps can reference each other&apos;s output values or pipe the stream of data to a transformation or a write step using the &lt;code&gt;forward_to&lt;/code&gt; directive. All of this can be visualized in the following graph[^2]:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/Graph.svg&quot; alt=&quot;Graph.svg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;You can configure scraping Prometheus metrics in a similar way:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 1. Discover Kubernetes Pods w/ custom selectors
discovery.kubernetes &quot;pods&quot; {
  role = &quot;pod&quot;
  
  selectors {
    role  = &quot;pod&quot;
    label = &quot;environment in (production)&quot;
  }
}

// 2. Collect metrics by scraping the Pods&apos; metrics endpoints
prometheus.scrape &quot;pods&quot; {
  targets    = discovery.kubernetes.pods.targets
  forward_to = [prometheus.relabel.keep_backend_only.receiver]
}

// 3. Transform data
prometheus.relabel &quot;keep_backend_only&quot; {
  rule {
    action        = &quot;keep&quot;
    source_labels = [&quot;app&quot;]
    regex         = &quot;backend&quot;
  }
  
  rule {
    action = &quot;labeldrop&quot;
    regex  = &quot;instance&quot;
  }
  
  forward_to = [prometheus.remote_write.grafana_cloud.receiver]
}

// 4. Write data
prometheus.remote_write &quot;grafana_cloud&quot; {
  endpoint {
    url = &quot;https://prometheus-prod-24-prod-eu-west-2.grafana.net/api/prom&quot;
    basic_auth {
      username = &quot;59482&quot;
      password = &quot;glc_eyJvI...&quot;
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/PrometheusGraph.svg&quot; alt=&quot;PrometheusGraph.svg&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;k8s-monitoring Chart&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;2025/11/01 Update&lt;/strong&gt; This post was written for the v1 version of the &lt;a href=&quot;https://github.com/grafana/k8s-monitoring-helm/tree/main/charts/k8s-monitoring-v1&quot;&gt;k8s-monitoring chart&lt;/a&gt;. v2 was released few days ago, which includes several breaking changes, a migration guide can be found &lt;a href=&quot;https://github.com/grafana/k8s-monitoring-helm/blob/main/charts/k8s-monitoring/docs/Migration.md&quot;&gt;here&lt;/a&gt;. The general concepts still apply, it&apos;s just the overall Helm template structure that was changed.&lt;/p&gt;
&lt;p&gt;The Grafana Cloud Kubernetes Integration requires data from the aforementioned log sources, as well as metrics from several agents.
Other than Alloy itself, the k8s-monitoring chart also deploys:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/kubernetes/kube-state-metrics&quot;&gt;kube-state-metrics&lt;/a&gt; – provides metrics for Kuberentes itself, e.g. amount of Pods, Services, etc&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/prometheus/node_exporter&quot;&gt;node_exporter&lt;/a&gt; – provides Linux-specific host metrics, e.g. CPU / Memory usage&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/opencost/opencost&quot;&gt;opencost&lt;/a&gt; – cost monitoring tool that I ended up disabling&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/sustainable-computing-io/kepler&quot;&gt;kepler&lt;/a&gt; – system power usage monitor that I also ended up disabling&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The chart also configures Alloy to collect some extra stuff, e.g. &lt;a href=&quot;https://github.com/google/cadvisor&quot;&gt;cAdvisor&lt;/a&gt; metrics which are scraped from the &lt;a href=&quot;https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/&quot;&gt;Kubelet&lt;/a&gt; process on each node.
You can definitely deploy all of these by yourself and build the necessary Alloy configuration blocks, but this chart does all of it for you.
The bare-minimum chart config for me is this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;metrics:
    enabled: true
    node-exporter:
        enabled: true # enabled by default
    kube-state-metrics:
	    enabled: true # enabled by default
logs:
    enabled: true
    pod_logs:
        enabled: true # enabled by default
    cluster_events:
        enabled: true # enabled by default
prometheus-node-exporter:
    enabled: true # enabled by default
prometheus-operator-crds:
    enabled: true # enabled by default
kepler:
    enabled: false # I don&apos;t need kepler
opencost:
    enabled: false # I don&apos;t need opencost
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Most of the config above can be removed, as the default Helm values are pretty good out of the box, I just like to be explicit. The only thing you&apos;d have to set is your Grafana Cloud credentials (or whatever remote write service you want to use).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;externalServices:
    prometheus:
        host: https://prometheus-prod-24-prod-eu-west-2.grafana.net
        basicAuth:
            username: &quot;3245224&quot;
            password: &quot;...&quot;
    loki:
        host: https://logs-prod-012.grafana.net
        basicAuth:
            username: &quot;127038&quot;
            password: &quot;...&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After removing my kube-prometheus-stack and loki-stack deployments in favor of this, I saw a significant memory usage drop – Prometheus, Grafana, Loki, Promtail were taking up to ~2GB, meanwhile, the Alloy deployment itself takes up only 400MB. CPU usage also went down but I don&apos;t have my old metrics – Alloy currently seems to be chilling at 0.02 CPU according to my metrics[^3].&lt;/p&gt;
&lt;p&gt;On low-spec devices, such as the Raspberry Pi, this setup can free up some more room for your &lt;em&gt;actual&lt;/em&gt; workloads.&lt;/p&gt;
&lt;h1&gt;Extra Integrations&lt;/h1&gt;
&lt;p&gt;If you want to introduce extra observability, e.g. metrics for a Postgres database, usually you&apos;d have to deploy the &lt;a href=&quot;https://github.com/prometheus-community/postgres_exporter&quot;&gt;postgres_exporter&lt;/a&gt;, make sure your Prometheus instance scrapes that, etc.
Alloy, however, has many &lt;a href=&quot;https://grafana.com/docs/alloy/latest/reference/components/prometheus/&quot;&gt;built-in exporters&lt;/a&gt;, including one for Postgres. This means that with just a few lines, you can have access to your metrics:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;prometheus.exporter.postgres &quot;production&quot; {
  data_source_names = [&quot;postgresql://b64:bWFpa2F0aQ==@localhost:5432/production?sslmode=disable&quot;]
}

prometheus.scrape &quot;postgres&quot; {
  targets    = prometheus.exporter.postgres.production.targets
  forward_to = [prometheus.remote_write.grafana_cloud.receiver]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Combining this with the &lt;a href=&quot;https://grafana.com/docs/grafana-cloud/monitor-infrastructure/integrations/integration-reference/integration-postgres/&quot;&gt;Grafana Postgres Integration&lt;/a&gt;, you can get a pretty dashboard w/ alerts in case you aren&apos;t using a managed database service.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/System-Observability-20240828173434143.webp&quot; alt=&quot;System Observability-20240828173434143.webp&quot; /&gt;
&lt;img src=&quot;../../assets/System-Observability-20240828173457438.webp&quot; alt=&quot;System Observability-20240828173457438.webp&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I repeated the same process for my &lt;a href=&quot;https://github.com/minio/minio&quot;&gt;MinIO&lt;/a&gt; deployment and added the &lt;a href=&quot;https://grafana.com/docs/grafana-cloud/monitor-infrastructure/integrations/integration-reference/integration-minio/&quot;&gt;respective integration&lt;/a&gt;, resulting in my final config &lt;a href=&quot;https://github.com/fr3fou/home/blob/main/apps/alloy/alloy.yaml&quot;&gt;here&lt;/a&gt;.
Definitely explore the other dashboard &amp;amp; alert integrations in Grafana Cloud, especially the &lt;a href=&quot;https://grafana.com/docs/grafana-cloud/monitor-infrastructure/integrations/integration-reference/integration-linux-node/&quot;&gt;Linux Server&lt;/a&gt; one.&lt;/p&gt;
&lt;h1&gt;Further Reading&lt;/h1&gt;
&lt;p&gt;This blog post/ramble got kind of long, but there still are some interesting things I&apos;d like to mention.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;If you want to ensure high availability and/or are processing lots of data, Alloy can also be deployed in &lt;a href=&quot;https://grafana.com/docs/alloy/latest/get-started/clustering/&quot;&gt;cluster mode&lt;/a&gt;. This will allow you to horizontally scale Alloy across multiple nodes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you have an existing Prometheus deployment and you want to stream data out of it to another instance (or Mimir), you can set up &lt;a href=&quot;https://prometheus.io/docs/prometheus/latest/federation/&quot;&gt;federation&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;prometheus.scrape &quot;federation&quot; {
    targets = [{
        __address__ = &quot;...&quot;,
    }]
    forward_to   = [prometheus.remote_write.grafana_cloud.receiver]
    job_name     = &quot;federation&quot;
    honor_labels = true
    params       = {
        &quot;match[]&quot; = [&quot;{__name__=~\&quot;.+\&quot;}&quot;],
    }
    metrics_path = &quot;/federate&quot;
    scheme       = &quot;http&quot;

    authorization {
        type        = &quot;Bearer&quot;
        credentials = env(&quot;FEDERATION_CREDENTIALS&quot;)
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Consider &lt;a href=&quot;https://grafana.com/docs/alloy/latest/set-up/deploy/&quot;&gt;different deployment methods&lt;/a&gt; based on your usage &amp;amp; infrastructure.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Alloy also performs monitoring on &lt;a href=&quot;https://grafana.com/docs/alloy/latest/collect/metamonitoring/&quot;&gt;itself&lt;/a&gt;. You can add the Alloy Grafana Integration for alerts (or set them up yourself) if it goes down. This can be quite useful in order to know whether an actual service you monitor is down or if Alloy hasn&apos;t been sending &lt;em&gt;any&lt;/em&gt; metrics.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Alloy can also collect traces using their &lt;a href=&quot;https://grafana.com/docs/alloy/latest/reference/components/otelcol/otelcol.receiver.zipkin/&quot;&gt;Zipkin&lt;/a&gt;, &lt;a href=&quot;https://grafana.com/docs/alloy/latest/reference/components/otelcol/otelcol.receiver.jaeger/&quot;&gt;Jaeger&lt;/a&gt; or &lt;a href=&quot;https://grafana.com/docs/alloy/latest/reference/components/otelcol/otelcol.receiver.otlp/&quot;&gt;OTLP&lt;/a&gt; receivers and send them to &lt;a href=&quot;https://grafana.com/docs/tempo/latest/configuration/grafana-alloy/&quot;&gt;Tempo&lt;/a&gt;. I haven&apos;t played around much with traces before, but would love to soon. You can also collect profiling data and send it to &lt;a href=&quot;https://grafana.com/docs/pyroscope/latest/configure-client/grafana-agent/&quot;&gt;Pyroscope&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Setting up &lt;a href=&quot;https://grafana.com/products/cloud/alerting/&quot;&gt;alerting&lt;/a&gt; in Grafana Cloud was quite easy – I added my Telegram Bot as a contact point &amp;amp; notification policy and stuff worked right away.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;[^1]: &lt;a href=&quot;https://opentelemetry.io/docs/what-is-opentelemetry/&quot;&gt;OTEL&lt;/a&gt; is a framework/standard for managing metrics/logs/traces.
[^2]: Grafana Alloy also comes with a &lt;a href=&quot;https://grafana.com/docs/alloy/latest/troubleshoot/debug/&quot;&gt;web interface&lt;/a&gt; for debugging which provides an entire graph of your blocks
[^3]: Your mileage may vary, memory and CPU usage are based on the amount of metrics and data you process. You can also definitely fine-tune the kube-prometheus-stack and loki-stack deployments, but I&apos;m fine with offloading this to someone else (the &quot;Cloud&quot;).&lt;/p&gt;
</content:encoded></item><item><title>Tools</title><link>https://simo.sh/notes/tools?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/notes/tools?utm_source=rss</guid><description>Evolving collection of tools/SaaS platforms</description><pubDate>Thu, 29 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This is an evolving personal collection of random tools/SaaS platforms that I&apos;m keeping an eye on.&lt;/p&gt;
&lt;h1&gt;Email&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/useplunk/plunk&quot;&gt;Plunk&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;open-source&lt;/li&gt;
&lt;li&gt;self-hostable&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Notification Engines&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://novu.co/&quot;&gt;Novu&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Open Source, can be self hosted for free
&lt;ul&gt;
&lt;li&gt;https://github.com/novuhq/novu&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Seems to be quite early in development
&lt;ul&gt;
&lt;li&gt;As of now, it&apos;s v0.22.0&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://knock.app/&quot;&gt;Knock&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Closed Source, Paid&lt;/li&gt;
&lt;li&gt;Seems to be more mature than Novu&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Finance&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.uselotus.io/&quot;&gt;Lotus&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Meant for creating pricing plans and managing them
&lt;ul&gt;
&lt;li&gt;Can implement many different pricing models, e.g. usage-based, seat-based, etc&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Open Source, can be self hosted&lt;/li&gt;
&lt;li&gt;Has support for many payment providers, e.g. Stripe &amp;amp; Braintree&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.getlago.com/&quot;&gt;Lago&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Same idea as Lotus, not sure which one is better&lt;/li&gt;
&lt;li&gt;Open Source, can be self hosted&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://runway.com/&quot;&gt;Runway Financial&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Tool for managing company finances, expenses, plans for growth, etc&lt;/li&gt;
&lt;li&gt;Whitelisted / Early-Access currently&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mercury.com/&quot;&gt;Mercury&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://midday.ai/&quot;&gt;Midday&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/maybe-finance/maybe&quot;&gt;Maybe&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;personal finance app&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://invoice.tolahq.com/&quot;&gt;Invoice Tola&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://antimetal.com/&quot;&gt;Antimetal&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;AWS Invoice Billing thing&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Real-Time&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://liveblocks.io/&quot;&gt;Liveblocks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://replicache.dev/&quot;&gt;Replicache&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.convex.dev/&quot;&gt;Convex&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.evolu.dev/&quot;&gt;Evolu&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;CRM&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://twenty.com/&quot;&gt;Twenty&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Fully open source&lt;/li&gt;
&lt;li&gt;Requires 2 containers (worker &amp;amp; main app) &amp;amp; a Postgres database&lt;/li&gt;
&lt;li&gt;Polished UI&lt;/li&gt;
&lt;li&gt;Can build custom objects and relationships easily in their object manager&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Application Monitoring&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://signoz.io/&quot;&gt;SigNoz&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Open Source, can be self hosted
&lt;ul&gt;
&lt;li&gt;Requires a bunch of services / containers to be deployed, e.g. Clickhouse&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Looks cheap&lt;/li&gt;
&lt;li&gt;All-in-one APM
&lt;ul&gt;
&lt;li&gt;Logs, Metrics, Exception tracking, Dashboards, etc&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://qryn.metrico.in/#/introduction&quot;&gt;qryn&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Open Source, can be self hosted&lt;/li&gt;
&lt;li&gt;All-in-one tool for a everything - logs, tracing, metrics, etc, seems to be built on top of Clickhouse&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/hyperdxio/hyperdx&quot;&gt;hyperdx&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Open Source, can be self hosted&lt;/li&gt;
&lt;li&gt;All-in-one APM&lt;/li&gt;
&lt;li&gt;UI seems better than SigNoz&lt;/li&gt;
&lt;li&gt;Seems to be less mature than SigNoz?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://uptrace.dev/&quot;&gt;uptrace&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Open Source, can be self hosted&lt;/li&gt;
&lt;li&gt;All-in-one APM&lt;/li&gt;
&lt;li&gt;Uses Clickhouse&lt;/li&gt;
&lt;li&gt;Exposes Prometheus-compatible endpoints&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://baselime.io/&quot;&gt;baselime&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Acquired by Cloudflare&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/coroot/coroot&quot;&gt;coroot&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;All in one APM&lt;/li&gt;
&lt;li&gt;Open Source&lt;/li&gt;
&lt;li&gt;Relies on eBPF&lt;/li&gt;
&lt;li&gt;Depends on Postgres/SQLite, Prometheus, Clickhouse&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Internal Tool Builders &amp;amp; Workflow Engines&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://directus.io/&quot;&gt;Directus&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Open Source, can be self hosted&lt;/li&gt;
&lt;li&gt;Can be used as a CMS or a generic BaaS&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.windmill.dev/&quot;&gt;Windmill&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Much&lt;/em&gt; more than just an internal tool builder. can build workflows / scripts / dashboards using code, etc&lt;/li&gt;
&lt;li&gt;Can write scripts in go/typescript/deno/bun/php/python&lt;/li&gt;
&lt;li&gt;Simple deployment architecture, requires only Postgres&lt;/li&gt;
&lt;li&gt;Has many integrations in the Windmill hub&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;File Storage&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.cloudflare.com/en-gb/products/cloudflare-images/&quot;&gt;Cloudflare Images&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Can automatically resize / pre-process images for serving to users&lt;/li&gt;
&lt;li&gt;Removes the need of your own custom image processing pipeline&lt;/li&gt;
&lt;li&gt;Cheap?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Data Analysis / Analytics&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://logsnag.com/&quot;&gt;LogSnag&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Looks sick, but it&apos;s bit expensive&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://clarity.microsoft.com/&quot;&gt;Clarity&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Free analytics &amp;amp; heatmap&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://evidence.dev/&quot;&gt;Evidence&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Open Source&lt;/li&gt;
&lt;li&gt;Uses Markdown&lt;/li&gt;
&lt;li&gt;Easily shareable / deployable SQL-based dashboards&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://usefathom.com/utm-builder&quot;&gt;UTM Builder&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.rilldata.com/&quot;&gt;Rill&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hex.tech/&quot;&gt;Hex&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.metabase.com/&quot;&gt;Metabase&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Open source, easy to deploy&lt;/li&gt;
&lt;li&gt;Very nice builder - easy to use by non-devs&lt;/li&gt;
&lt;li&gt;Polished UI&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://superset.apache.org/&quot;&gt;Superset&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Open source, easy to deploy as well
&lt;ul&gt;
&lt;li&gt;Uses a worker based architecture, will deploy several containers&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Has a lot more built in charts than Metabase, pretty sure it uses &lt;a href=&quot;https://echarts.apache.org/en/index.html&quot;&gt;ECharts&lt;/a&gt; under the hood&lt;/li&gt;
&lt;li&gt;Configuration was kind of confusing - you use Python code for this 🤔. will have to play around with it more to see how I feel about it&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://marimo.io/&quot;&gt;Marimo&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Seems like a better version of Jupyter notebook&lt;/li&gt;
&lt;li&gt;Has really cool features like the embeddings visualiser&lt;/li&gt;
&lt;li&gt;Can be used for making internal tools&lt;/li&gt;
&lt;li&gt;Cells aren&apos;t &quot;linear&quot; but instead build a graph of all the dependencies - you can add sliders / variables and when you update them, cells get automatically updated&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;AI&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.rerun.io/&quot;&gt;Rerun&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Tool for visualising data from ML models&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://lumalabs.ai/&quot;&gt;Luma AI&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;3D capture scanning tool&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/mlflow/mlflow&quot;&gt;mlflow&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Open Source MLOps tooling, similar to weights and biases&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;IDP&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.getport.io/&quot;&gt;Port&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Decent free tier&lt;/li&gt;
&lt;li&gt;k8s integration available
&lt;ul&gt;
&lt;li&gt;https://docs.getport.io/build-your-software-catalog/sync-data-to-catalog/kubernetes/&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.cortex.io/&quot;&gt;Cortex&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.porter.run/&quot;&gt;Porter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.nullstone.io/&quot;&gt;Nullstone&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.withcoherence.com/&quot;&gt;Coherence&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://northflank.com/&quot;&gt;Northflank&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Document Management&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.papermark.io/&quot;&gt;Papermark&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Open Source alternative to DocSend&lt;/li&gt;
&lt;li&gt;Self Hostable
&lt;ul&gt;
&lt;li&gt;Although, it&apos;s &quot;Built with &lt;a href=&quot;http://vercel.com/storage&quot;&gt;Vercel Storage&lt;/a&gt; and &lt;a href=&quot;http://vercel.com/edge&quot;&gt;Vercel Edge Functions&lt;/a&gt;&quot;
&lt;ul&gt;
&lt;li&gt;Hopefully it&apos;s not tightly coupled with Vercel&apos;s infrastructure&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Developer Tools&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://graphite.dev/&quot;&gt;Graphite&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Implements a Git workflow called &lt;a href=&quot;https://stacking.dev/&quot;&gt;stacking&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://harlequin.sh/&quot;&gt;Harlequin&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;TUI tool for data exploration with support for many databases&lt;/li&gt;
&lt;li&gt;Can also write queries / scroll through results / etc&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Databases&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://tembo.io/&quot;&gt;Tembo&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Postgres based&lt;/li&gt;
&lt;li&gt;Comes in with &lt;em&gt;many&lt;/em&gt; built in extensions&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Backups&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/eduardolat/pgbackweb&quot;&gt;pgbackweb&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Diagram Makers&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://chartdb.io/&quot;&gt;ChartDB&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dbdiagram.io/home&quot;&gt;dbdiagram&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Video&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://wide.video/&quot;&gt;Wide Video&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Free online Video Editor&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://remotion.dev/&quot;&gt;Remotion&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Make videos using code / React&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tools.rotato.app/&quot;&gt;Rotato Tools&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;A bunch of free tools for compression / transparency / metadata, etc&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Background Process Engines&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://trigger.dev/&quot;&gt;Trigger&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Self hostable, Open Source&lt;/li&gt;
&lt;li&gt;Uses &lt;a href=&quot;https://github.com/graphile/worker&quot;&gt;Graphile&lt;/a&gt; as the task engine for Postgres&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Mockups / Graphics&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://raytracing.io/&quot;&gt;Raytracing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.shots.so/&quot;&gt;shots.so&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Free&lt;/li&gt;
&lt;li&gt;Pretty good&lt;/li&gt;
&lt;li&gt;Can&apos;t position the mocks when you drag them in, unfortunately&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://rotato.app/&quot;&gt;rotato&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://isometricshot.com/&quot;&gt;Isometric Shot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://postspark.app/&quot;&gt;PostSpark&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://previewed.app/&quot;&gt;Previewed&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Feature Flags&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://openfeature.dev/&quot;&gt;OpenFeature&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.hypertune.com/&quot;&gt;Hypertune&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;CMS&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://keystatic.com/&quot;&gt;Keystatic&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;not exactly a CMS&lt;/li&gt;
&lt;li&gt;just an admin ui for your static astro / next.js / etc apps&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://payloadcms.com/&quot;&gt;Payload&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;2.0 version is alright
&lt;ul&gt;
&lt;li&gt;Used it for &lt;a href=&quot;../projects/FindAudit.md&quot;&gt;FindAudit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Postgres adapter was very hit/miss&lt;/li&gt;
&lt;li&gt;Hit several issues and bugs, reported a bunch of them&lt;/li&gt;
&lt;li&gt;Was tricky to setup structured contextual logging&lt;/li&gt;
&lt;li&gt;Would use it again for projects that aren&apos;t as dynamic/user facing as FindAudit&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;3.0 version will be a Next.js app which means you can integrate it into any existing Next.js app easily
&lt;ul&gt;
&lt;li&gt;seems to be in beta for half a year or so now&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;TODO Apps&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://tweek.so/&quot;&gt;Tweek&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;simple calendar view&lt;/li&gt;
&lt;li&gt;can print your schedule&lt;/li&gt;
&lt;li&gt;no bs&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Diagram Editors&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://monodraw.helftone.com/&quot;&gt;Monodraw&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;paid&lt;/li&gt;
&lt;li&gt;macOS only&lt;/li&gt;
&lt;li&gt;ascii plain text editor for diagrams&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;User Feedback&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://featureos.app/&quot;&gt;featureOS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.featurebase.app/&quot;&gt;Featurebase&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://feedback.fish/&quot;&gt;Feedback Fish&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://userjot.com/&quot;&gt;UserJot&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Proxies&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.webshare.io/&quot;&gt;Webshare&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Activation Function</title><link>https://simo.sh/study/activation-function?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/study/activation-function?utm_source=rss</guid><pubDate>Thu, 18 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;An activation function in a machine learning model is used to add &lt;a href=&quot;./Non-linearity.md&quot;&gt;Non-linearity&lt;/a&gt; to the output.
This is needed, because otherwise, our model will learn to act as a linear least squares model. We usually want our models to solve complex, non-linear problems.&lt;/p&gt;
&lt;h1&gt;Examples&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;ReLU - $f(x) = max(0, x)$&lt;/li&gt;
&lt;li&gt;Sigmoid - $f(x) = \frac{1}{1+e^{-x}}$ ^3aed71&lt;/li&gt;
&lt;li&gt;Identity - $f(x) = x$&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;References&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;https://www.wikiwand.com/en/Activation_function&lt;/li&gt;
&lt;li&gt;https://www.quora.com/What-do-you-mean-by-introducing-non-linearity-in-a-neural-network&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Arithmetic Mean</title><link>https://simo.sh/study/arithmetic-mean?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/study/arithmetic-mean?utm_source=rss</guid><pubDate>Thu, 18 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The arithmetic mean, also known as average is the most commonly used mean
$$
\frac{{\sum_{i=1}^{n} x_i}}{n}
$$
You sum up all points in the data set and then you divide them by the number of data points.&lt;/p&gt;
</content:encoded></item><item><title>Binary Classification</title><link>https://simo.sh/study/binary-classification?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/study/binary-classification?utm_source=rss</guid><pubDate>Thu, 18 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Binary classification is the problem of classifying the class of an element between 2 sets.&lt;/p&gt;
&lt;h1&gt;Example&lt;/h1&gt;
&lt;p&gt;A simples example for such a problem would be to classify whether a point lies below or above a line in a coordinate system.
&lt;img src=&quot;../../assets/binary-classification.png&quot; alt=&quot;99_pct_train.png|600&quot; /&gt;
Another example would be to determine whether an image is a photo of a hot dog or not a hot dog.&lt;/p&gt;
&lt;h1&gt;References&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;https://www.wikiwand.com/en/Binary_classification&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Convolutional Neural Network</title><link>https://simo.sh/study/convolutional-neural-network?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/study/convolutional-neural-network?utm_source=rss</guid><pubDate>Thu, 18 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;./Neural-Network.md&quot;&gt;Neural Network&lt;/a&gt; type used primarily for image or video data.  It uses an &lt;a href=&quot;./Image-Kernel.md&quot;&gt;Image Kernel&lt;/a&gt; that convolves / strides across the input. Each value in the kernel is a weight in the neural network.&lt;/p&gt;
&lt;p&gt;CNNs break down the input into smaller bits, instead of processing the entire image at once. These smaller bits are called features and the process of finding the exact kernels that detect those features is called feature extraction.&lt;/p&gt;
&lt;h1&gt;Example&lt;/h1&gt;
&lt;p&gt;A convolutional layer is configured by setting the kernel amount, size and stride.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  # Example with Tensorflow &amp;amp; Keras
  filters = 32
  size = (3, 3)
  input_shape = (226, 226, 3) # 226x226 RGB image
  keras.layers.Conv2D(filters, size, strides=2, input_shape=input_shape, activation=tf.nn.relu)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Increasing the stride will result in a smaller output of the layers&lt;/p&gt;
&lt;h1&gt;References&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;https://www.wikiwand.com/en/Convolutional_neural_network&lt;/li&gt;
&lt;li&gt;https://keras.io/api/layers/convolution_layers/convolution2d/&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Cost Function</title><link>https://simo.sh/study/cost-function?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/study/cost-function?utm_source=rss</guid><pubDate>Thu, 18 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A cost function (also known as a loss function) measures how wrong a machine learning model is.&lt;/p&gt;
&lt;p&gt;It&apos;s different from the error of a neural network in that the error simply measures how far off a &lt;em&gt;single guess&lt;/em&gt; was from the real answer. The loss function on the other hand tells you how bad it is that it failed to recognize something &lt;em&gt;and&lt;/em&gt; you average the result over the entire data set (or batch).&lt;/p&gt;
&lt;p&gt;It is an important distinction because in some contexts, you might want to punish certain results more, e.g getting a false positive might be worse than getting a false negative. In other cases a simple squared error will do just fine.&lt;/p&gt;
&lt;p&gt;It is used for optimising the model by computing its derivative and using that for adjusting the weights and biases.&lt;/p&gt;
&lt;h1&gt;Examples&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;Mean Squared Error - $\frac{1}{n} \sum_{i=1}^{n} (y_i - \hat{y_i})^2$ where $y_i$ and $\hat{y_i}$ respectively mean the output of the model and the expected output, and $n$ is the total number of samples. ^07648b&lt;/li&gt;
&lt;li&gt;Binary Cross-Entropy - $-\frac{1}{n}\sum_{i=1}^n [y_i\log(\hat{y_i}) + (1-y_i)\log(1-\hat{y_i})]$ ^8ef262&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>F1 score</title><link>https://simo.sh/study/f1-score?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/study/f1-score?utm_source=rss</guid><pubDate>Thu, 18 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The F-score represents a test&apos;s accuracy. It&apos;s computed by taking the &lt;a href=&quot;./Harmonic-Mean.md&quot;&gt;Harmonic Mean&lt;/a&gt; of the &lt;a href=&quot;./Precision-and-Recall.md&quot;&gt;Precision and Recall&lt;/a&gt;.
$$
F_1 = \frac{2}{\frac{1}{Precision}+\frac{1}{Recall}}
$$
The result will be between $0.0$ (meaning being &lt;em&gt;completely&lt;/em&gt; inaccurate) and $1.0$ (meaning being &lt;em&gt;completely&lt;/em&gt; perfect).&lt;/p&gt;
&lt;h1&gt;References&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;https://www.wikiwand.com/en/F1_score&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Forward Propagation</title><link>https://simo.sh/study/forward-propagation?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/study/forward-propagation?utm_source=rss</guid><pubDate>Thu, 18 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The forward pass in a &lt;a href=&quot;./Neural-Network.md&quot;&gt;Neural Network&lt;/a&gt; is also known as the &quot;prediction&quot; step. You give the neural network some input and it gives you an output.&lt;/p&gt;
&lt;h1&gt;How it works&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;Passing in the input to the neural network
&lt;ul&gt;
&lt;li&gt;The input shape has to match with the first layer of the network&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The $j_{th}$ layer&apos;s activations are equal to $z_j = \sigma(w_{jk}a_{jk} + b_j)$
&lt;ul&gt;
&lt;li&gt;In other words, the activation of the current layer&apos;s $j_{th}$ neuron is equal to the weighted sum of the previous layer&apos;s weights and activations + a bias which are all then passed to an &lt;a href=&quot;./Activation-Function.md&quot;&gt;Activation Function&lt;/a&gt;. &lt;img src=&quot;../../assets/neural-network-matrix.png&quot; alt=&quot;1000&quot; /&gt;
&lt;ul&gt;
&lt;li&gt;The weighted sum is computed by performing matrix multiplication between the weight matrix and the previous layer&apos;s activations.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The input layer&apos;s &quot;activations&quot; are simply the input values.&lt;/li&gt;
&lt;li&gt;This is repeated for all layers&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Gradient Descent</title><link>https://simo.sh/study/gradient-descent?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/study/gradient-descent?utm_source=rss</guid><pubDate>Thu, 18 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Gradient Descent is the most popular &lt;a href=&quot;./Optimizer.md&quot;&gt;Optimizer&lt;/a&gt; for machine learning models.&lt;/p&gt;
&lt;h1&gt;How it works&lt;/h1&gt;
&lt;p&gt;It involves the following steps&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Go over the entire dataset&lt;/li&gt;
&lt;li&gt;Compute the gradients of the weights &amp;amp; biases, using &lt;a href=&quot;Backward-Propagation.md&quot;&gt;Backward-Propagation&lt;/a&gt; with respect to the &lt;a href=&quot;./Cost-Function.md&quot;&gt;Cost Function&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Average the gradients over the entire dataset&lt;/li&gt;
&lt;li&gt;Add the gradients to the weights &amp;amp; biases while also multiplying them by a &lt;a href=&quot;./Learning-Rate.md&quot;&gt;Learning Rate&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Batching&lt;/h1&gt;
&lt;p&gt;You can introduce some form of batching to the process&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Mini-batch Gradient Descent&lt;/strong&gt; allows you to adjust the parameters every N batches instead of after processing the entire dataset. This is less computationally expensive.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stochastic Gradient Descent&lt;/strong&gt; is like &lt;strong&gt;Mini-batch Gradient Descent&lt;/strong&gt;, but with N = 1. This means that you tweak the parameters after every sample in the data set.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Harmonic Mean</title><link>https://simo.sh/study/harmonic-mean?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/study/harmonic-mean?utm_source=rss</guid><pubDate>Thu, 18 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The harmonic mean can be represented in terms of the &lt;a href=&quot;./Arithmetic-Mean.md&quot;&gt;Arithmetic Mean&lt;/a&gt;. It is useful if you&apos;re interested in the rate (e.g speed) of the data.
$$
\frac{n}{\sum_{i=1}^{n} \frac{1}{x_i}}
$$
You divide the number of data points by the sum of the reciprocals of all the points in the data set.&lt;/p&gt;
&lt;h1&gt;Examples&lt;/h1&gt;
&lt;p&gt;Let&apos;s say you want to calculate the average speed of a car on a race track, based on measurements taken each lap.
The first lap the car was driving at $120km/h$ and the second lap, at $100km/h$.&lt;/p&gt;
&lt;p&gt;Because we have a fixed distance (the track length) and varying time (speed is proportional to time), the speed is the rate we want to calculate.&lt;/p&gt;
&lt;p&gt;$$
\frac{2}{\frac{1}{120km/h}+\frac{1}{100km/h}} = \frac{2}{\frac{11}{600}}\approx109km/h
$$&lt;/p&gt;
&lt;h1&gt;References&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;https://youtu.be/jXKYI7wyqp0&lt;/li&gt;
&lt;li&gt;https://www.wikiwand.com/en/Harmonic_mean&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Huffman Coding</title><link>https://simo.sh/study/huffman-coding?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/study/huffman-coding?utm_source=rss</guid><pubDate>Thu, 18 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Huffman Trees are a way to losslessly compress text by encoding the probabilities of each character in a tree. This allows you to use less bits for more frequently used characters, thus saving space. It is the most efficient way to compress text &lt;em&gt;if you look at one character at a time&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;You build the tree by going over the text input and storing the frequency of each character in a priority queue. You then start pairing up the 2 least frequently used characters (the 2 top-most elements in the queue) in a new node, whose value is the sum of their frequencies. You repeat this process recursively until you&apos;ve merged all nodes into one tree (you&apos;ll be left with one node in the queue).&lt;/p&gt;
&lt;p&gt;After you&apos;ve built the tree you then encode the original input text into a new stream of bits. You build a dictionary of each letter and its corresponding binary representation. This is done by iterating over the tree (for each leaf node) and storing a 0 or a 1 depending on whether you went left or right. You compress the data by going over each character and using the dictionary, writing a 0 or a 1.
You can then send the compressed data &lt;em&gt;and&lt;/em&gt;  the translation dictionary to the recipient.&lt;/p&gt;
&lt;p&gt;To decompress the data you to the same operations in reverse. By going over the input stream and using the dictionary you can easily lookup the corresponding character.&lt;/p&gt;
&lt;p&gt;The huffman code dictionary isn&apos;t fixed length (thus takes up less space as you don&apos;t have to use padding) and therefore posses the &lt;a href=&quot;./Prefix-Property.md&quot;&gt;prefix property&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;Example&lt;/h1&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/Huffman_coding_visualisation.svg&quot; alt=&quot;Huffman_coding_visualisation.svg&quot; /&gt;&lt;/p&gt;
&lt;h1&gt;References&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;https://youtu.be/Lto-ajuqW3w&lt;/li&gt;
&lt;li&gt;https://youtu.be/M5c_RFKVkko&lt;/li&gt;
&lt;li&gt;https://www.youtube.com/watch?v=JsTptu56GM8&lt;/li&gt;
&lt;li&gt;https://www.wikiwand.com/en/Huffman_coding&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Hyper Parameters</title><link>https://simo.sh/study/hyper-parameters?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/study/hyper-parameters?utm_source=rss</guid><pubDate>Thu, 18 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Hyper-Parameters are global parameters in machine learning that aren&apos;t adjusted during the learning process. Instead, they are manually configured by the author of the model.&lt;/p&gt;
&lt;p&gt;You can use a &lt;a href=&quot;Grid%20Search.md&quot;&gt;Grid Search&lt;/a&gt; to automate the finding of the optimal values.&lt;/p&gt;
</content:encoded></item><item><title>Image Kernel</title><link>https://simo.sh/study/image-kernel?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/study/image-kernel?utm_source=rss</guid><pubDate>Thu, 18 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;An image kernel (also known as a filter) is a small matrix that is used to apply filters to an image, e.g edge detection, sharpening, blurring, etc.
They are typically square arrays that consist of numbers which let you compute a given pixel value using only the surrounding neighbours&apos; values.&lt;/p&gt;
&lt;h1&gt;Examples&lt;/h1&gt;
&lt;p&gt;Example filter that makes an image more &quot;sharp&quot;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[
	[0, -1, 0],
  	[-1, 5,-1],
  	[0, -1, 0],
]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/cheems.jpg&quot; alt=&quot;cheems.jpg|500&quot; /&gt;&lt;img src=&quot;../../assets/cheems-sharped.jpg&quot; alt=&quot;cheems_edge.jpg|500&quot; /&gt;&lt;/p&gt;
&lt;h1&gt;How it works&lt;/h1&gt;
&lt;p&gt;This is achieved by walking / striding / convolving over the source image and building each new pixel value by multiplying the respective values and summing them up
&lt;img src=&quot;../../assets/image-kernel.png&quot; alt=&quot;750710_QS1ArBEUJjjySXhE.png|800&quot; /&gt;
&lt;img src=&quot;../../assets/image-kernel-2.png&quot; alt=&quot;image.png|800&quot; /&gt;
The stride amount defines the step size when convolving across the input image. A higher stride will result in a smaller output.&lt;/p&gt;
&lt;h1&gt;References&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;https://setosa.io/ev/image-kernels/&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Learning Rate</title><link>https://simo.sh/study/learning-rate?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/study/learning-rate?utm_source=rss</guid><pubDate>Thu, 18 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The learning rate is a small number, usually denoted as $\alpha$ which is used to decrease / control the &quot;nudge&quot; / adjustment of the parameters of a machine learning model.&lt;/p&gt;
&lt;p&gt;It is required because making too big adjustments might cause  you to over-shoot your target.
Making it too small however on the other hand will cause the model to converge too slowly.&lt;/p&gt;
&lt;p&gt;It is one of many &lt;a href=&quot;./Hyper-Parameters.md&quot;&gt;Hyper-Parameters&lt;/a&gt; in an &lt;a href=&quot;./Optimizer.md&quot;&gt;Optimizer&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Linear System</title><link>https://simo.sh/study/linear-system?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/study/linear-system?utm_source=rss</guid><pubDate>Thu, 18 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A linear system is a collection of multiple Linear &lt;a href=&quot;Equations.md&quot;&gt;Equations&lt;/a&gt; with the same variables. Finding the solution to a linear system involves finding the right variables, such that all equations are satisfied.&lt;/p&gt;
&lt;h1&gt;Examples&lt;/h1&gt;
&lt;p&gt;${\displaystyle {\begin{cases}3x+2y-z=1\2x-2y+4z=-2\-x+{\frac {1}{2}}y-z=0\end{cases}}}$
With the solution being:
${\displaystyle (x,y,z)=(1,-2,-2)}$&lt;/p&gt;
&lt;p&gt;If you plot all of the equations, the solution lies in the intersection of their lines / planes. It could be a single number or an entire set. If there isn&apos;t a common point across &lt;em&gt;all&lt;/em&gt; of them – then there are no solutions.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/Pasted-image-20230822102553.png&quot; alt=&quot;400&quot; /&gt;&lt;/p&gt;
&lt;p&gt;${\displaystyle {\begin{cases}x-y=1\3x+y=9\end{cases}}}$
With the solution being:
${\displaystyle (x,y)=(2,3)}$&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/Pasted-image-20230822102653.png&quot; alt=&quot;400&quot; /&gt;&lt;/p&gt;
&lt;h1&gt;References&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;https://www.wikiwand.com/en/System_of_linear_equations&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Mean</title><link>https://simo.sh/study/mean?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/study/mean?utm_source=rss</guid><pubDate>Thu, 18 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The mean is a way to summarize a dataset using a single number&lt;/p&gt;
&lt;h1&gt;Types of mean&lt;/h1&gt;
&lt;h2&gt;&lt;a href=&quot;./Harmonic-Mean.md&quot;&gt;Harmonic Mean&lt;/a&gt;&lt;/h2&gt;
&lt;h2&gt;&lt;a href=&quot;./Arithmetic-Mean.md&quot;&gt;Arithmetic Mean&lt;/a&gt;&lt;/h2&gt;
</content:encoded></item><item><title>Multi label Classification</title><link>https://simo.sh/study/multi-label-classification?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/study/multi-label-classification?utm_source=rss</guid><pubDate>Thu, 18 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Multi-label classification is the problem where you try to assign multiple labels/classes to an element of a dataset.&lt;/p&gt;
&lt;h1&gt;Example&lt;/h1&gt;
&lt;p&gt;An example problem would be to classify all the instruments in a song.&lt;/p&gt;
</content:encoded></item><item><title>Neural Network Architecture</title><link>https://simo.sh/study/neural-network-architecture?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/study/neural-network-architecture?utm_source=rss</guid><pubDate>Thu, 18 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A &lt;a href=&quot;./Neural-Network.md&quot;&gt;Neural Network&lt;/a&gt; consists of many &quot;neurons&quot;, which are simply numbers. You can think of them as neurons from biology - they can be activated or not.&lt;/p&gt;
&lt;p&gt;These neurons build up layers. The first layer is your input, e.g if we take the problem of classifying images, you would take in an array of all the pixel values.&lt;/p&gt;
&lt;p&gt;These neurons are connected using weights and biases. These are the parameters of the neural network. Just like the neurons, they are also just numbers. The end goal of training a neural network is to find the parameters that best solve your problem.&lt;/p&gt;
&lt;p&gt;Each neuron from the previous layer is connected to every neuron in the next layer. &lt;em&gt;Generally&lt;/em&gt; you need more layers in order to solve more complicated tasks. The term deep learning comes from literally having a deep neural network, i.e using many layers. Having more layers and thus, more parameters, makes the learning process more computationally expensive. There are many different types of layers, each tailored to solving specific problems.&lt;/p&gt;
&lt;p&gt;At the end of each layer, you configure an &lt;a href=&quot;./Activation-Function.md&quot;&gt;Activation Function&lt;/a&gt;. At the last layer, you configure a &lt;a href=&quot;./Cost-Function.md&quot;&gt;Cost Function&lt;/a&gt; and an &lt;a href=&quot;./Optimizer.md&quot;&gt;Optimizer&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/neural-network-architecture.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;It is commonly represented using matrices (or the more general term, tensors). GPUs are optimized to compute many matrix operations in parallel, such as matrix multiplication (which is the core operation in neural networks), which is why they are used for training and running neural networks.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/neural-network-matrix.png&quot; alt=&quot;875&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Here we can see that the weights from the input layer that connect to $j_{th}$ neuron in the output layer, are all in the $j_{th}$ row of the weight matrix.&lt;/p&gt;
&lt;h1&gt;References&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;https://www.jeremyjordan.me/intro-to-neural-networks/&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Neural Network</title><link>https://simo.sh/study/neural-network?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/study/neural-network?utm_source=rss</guid><pubDate>Thu, 18 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A neural network, (also known as a Multi-layer &lt;a href=&quot;./Perceptron.md&quot;&gt;Perceptron&lt;/a&gt;) is a mathematical model that is used in machine learning.&lt;/p&gt;
&lt;p&gt;There are many ways to make a neural network learn, one of which is &lt;a href=&quot;./Supervised-Learning.md&quot;&gt;Supervised Learning&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The combination of the last layer&apos;s &lt;a href=&quot;./Activation-Function.md&quot;&gt;Activation Function&lt;/a&gt; and &lt;a href=&quot;./Cost-Function.md&quot;&gt;Cost Function&lt;/a&gt;, determine the type of the problem you&apos;re trying to solve, some examples include&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;./Binary-Classification.md&quot;&gt;Binary Classification&lt;/a&gt; uses &lt;a href=&quot;./Activation-Function.md#3aed71&quot;&gt;Sigmoid&lt;/a&gt; and &lt;a href=&quot;./Cost-Function.md#8ef262&quot;&gt;Binary Cross-Entropy&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;The output is a number between 0 and 1, you can then classify both classes by checking if the value is $&amp;lt; 0.5$ or $&amp;gt; 0.5$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;./Multi-label-Classification.md&quot;&gt;Multi-label Classification&lt;/a&gt; uses &lt;a href=&quot;./Activation-Function.md#3aed71&quot;&gt;Sigmoid&lt;/a&gt; and &lt;a href=&quot;./Cost-Function.md#8ef262&quot;&gt;Binary Cross-Entropy&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;The output is a vector of probabilities, each indicating a different label&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A neural network&apos;s &lt;a href=&quot;./Forward-Propagation.md&quot;&gt;Forward-Propagation&lt;/a&gt; is used for predicting data and its &lt;a href=&quot;Backward-Propagation.md&quot;&gt;Backward-Propagation&lt;/a&gt; used for training itself.&lt;/p&gt;
&lt;h1&gt;&lt;a href=&quot;./Neural-Network-Architecture.md&quot;&gt;Neural Network Architecture&lt;/a&gt;&lt;/h1&gt;
</content:encoded></item><item><title>Non linearity</title><link>https://simo.sh/study/non-linearity?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/study/non-linearity?utm_source=rss</guid><pubDate>Thu, 18 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A non-linear function is one that doesn&apos;t follow the requirements for a linear function. This means that it should abide by any of the following conditions&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It should be of a higher degree than 1 (have exponents higher than 1)&lt;/li&gt;
&lt;li&gt;Include operations that aren&apos;t addition - e.g multiplication, division, etc&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Examples&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;$f(x) = x^2$&lt;/li&gt;
&lt;li&gt;$f(x, y) = x^3 + y^2$&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;References&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.wikiwand.com/en/Nonlinear_system&quot;&gt;https://www.wikiwand.com/en/Nonlinear_system&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Optimizer</title><link>https://simo.sh/study/optimizer?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/study/optimizer?utm_source=rss</guid><pubDate>Thu, 18 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;An optimizer is an algorithm for adjusting the parameters of a machine learning model to minimize the &lt;a href=&quot;./Cost-Function.md&quot;&gt;Cost Function&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Examples&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;./Gradient-Descent.md&quot;&gt;Gradient Descent&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Adam&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Perceptron</title><link>https://simo.sh/study/perceptron?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/study/perceptron?utm_source=rss</guid><pubDate>Thu, 18 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The perceptron is a basic mathematical model for solving &lt;a href=&quot;./Binary-Classification.md&quot;&gt;Binary Classification&lt;/a&gt; problems.
It consists of 2 components:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Weights &amp;amp; Biases&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;You can think of these as knobs that you tweak to get the desired result&lt;/li&gt;
&lt;li&gt;They are just numbers&lt;/li&gt;
&lt;li&gt;The difference between both arises in the learning process&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;An &lt;a href=&quot;./Activation-Function.md&quot;&gt;Activation Function&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It&apos;s quite limited and can&apos;t solve anything that isn&apos;t linearly separable, for example the XOR problem.&lt;/p&gt;
&lt;h1&gt;Example&lt;/h1&gt;
&lt;p&gt;Let&apos;s solve a classic problem, determining whether a point lies below or above a line
&lt;img src=&quot;../../assets/binary-classification.png&quot; alt=&quot;99_pct_train.png|600&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The input of our perceptron will be the X and Y coordinates of the point.&lt;/li&gt;
&lt;li&gt;The output of our perceptron should be a number, let&apos;s say that $-1$ means that it&apos;s below the line and $1$, above the line.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Prediction&lt;/h1&gt;
&lt;p&gt;The feed forward (or in other words, the &quot;prediction&quot;) process involves several steps&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Provide some sort of input, $x$ and $y$&lt;/li&gt;
&lt;li&gt;Compute the weighted sum of the respective weights, $w_0x + w_1y$&lt;/li&gt;
&lt;li&gt;Add the bias, $w_0x + w_1y + b$&lt;/li&gt;
&lt;li&gt;Plug the result into an activation function $\operatorname{sgn}(w_0x + w_1y + b)$
$$
\operatorname{sgn}(x) =
\begin{cases}
-1 &amp;amp; \text{if } x &amp;lt; 0, \
0 &amp;amp; \text{if } x = 0, \
1 &amp;amp; \text{if } x &amp;gt; 0.
\end{cases}
$$&lt;/li&gt;
&lt;li&gt;The output will determine whether the point lies below or above the line&lt;/li&gt;
&lt;li&gt;The above steps can be summarized as $y = f(\sum_{i=0}^{n}w_ix_i + b)$&lt;/li&gt;
&lt;li&gt;At first, our perceptron will perform very poorly. This is because usually the weights are completely random.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Learning&lt;/h1&gt;
&lt;p&gt;We can use a simplified version of &lt;a href=&quot;./Supervised-Learning.md&quot;&gt;Supervised Learning&lt;/a&gt; to optimize our model.&lt;/p&gt;
&lt;p&gt;We can compute the error for our model - $error = y - \hat{y}$. We can then update our weights - $w_i \leftarrow w_i + \alpha \cdot error \cdot x_i$ where $\alpha$ is our &lt;a href=&quot;./Learning-Rate.md&quot;&gt;Learning Rate&lt;/a&gt; and $x$ is our input. We can then also update our bias - $b \leftarrow \alpha \cdot b \cdot error$.&lt;/p&gt;
&lt;p&gt;We repeat this process a bunch of times, until our model converges.&lt;/p&gt;
&lt;p&gt;An example implementation can be found on my &lt;a href=&quot;https://github.com/fr3fou/gone/blob/master/perceptron/perceptron.go&quot;&gt;GitHub&lt;/a&gt; repo&lt;/p&gt;
&lt;h1&gt;References&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;https://www.wikiwand.com/en/Perceptron&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Positional Encoding</title><link>https://simo.sh/study/positional-encoding?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/study/positional-encoding?utm_source=rss</guid><pubDate>Thu, 18 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;When performing a &lt;a href=&quot;./Vector-Embedding.md&quot;&gt;Vector Embedding&lt;/a&gt; of a sentence, you would like retain the positional information of each word when passing your input to your model. This way the model can learn to recognize different uses of the words in the sentence.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/Pasted-image-20231207191856.png&quot; alt=&quot;707&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The authors of the &lt;a href=&quot;https://arxiv.org/pdf/1706.03762.pdf&quot;&gt;Attention Is All You Need&lt;/a&gt; paper decided to use trigonometric functions to encode the positional information. The $cos$ and $sin$ functions have the nice property that the values are constrained between $[-1, 1]$. They also follow a pattern which means that the model can learn accordingly. This is the reason why we don&apos;t use the index of the token itself – it has a value in the range $[0, d]$ which means that words that appear further in the sentence will have a bigger value. They decided to encode odd vector indexes with $cos$ and even vector indexes with $sin$:
$$
\begin{align*}
\text{{PE}}(pos, 2i) &amp;amp;= \sin\left(\frac{{pos}}{{10000^{\frac{{2i}}{{d_{\text{{model}}}}}}}}\right) \
\text{{PE}}(pos, 2i+1) &amp;amp;= \cos\left(\frac{{pos}}{{10000^{\frac{{2i}}{{d_{\text{{model}}}}}}}}\right)
\end{align*}
$$
After calculating the positional embedding for the given position, it&apos;s added up together with the corresponding word vector embedding.&lt;/p&gt;
&lt;h1&gt;References&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/bCz4OMemCcA&quot;&gt;https://youtu.be/bCz4OMemCcA&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;https://machinelearningmastery.com/a-gentle-introduction-to-positional-encoding-in-transformer-models-part-1/&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Precision and Recall</title><link>https://simo.sh/study/precision-and-recall?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/study/precision-and-recall?utm_source=rss</guid><pubDate>Thu, 18 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Precision and recall are measurements for determining the accuracy of a prediction model.
&lt;img src=&quot;../../assets/precision-vs-recall-2.png&quot; alt=&quot;Precisionrecall.svg.png|300&quot; /&gt; &lt;img src=&quot;../../assets/precision-vs-recall.png&quot; alt=&quot;1_xMl_wkMt42Hy8i84zs2WGg.png|600&quot; /&gt;&lt;/p&gt;
&lt;h1&gt;Precision&lt;/h1&gt;
&lt;p&gt;Precision (also known as sensitivity) measures how many of the &lt;strong&gt;predicted positives&lt;/strong&gt; were actually &lt;strong&gt;positive&lt;/strong&gt; (correct).
$$
Precision = \frac{TP}{TP+FP}
$$
For example, let&apos;s say you have a &lt;a href=&quot;./Binary-Classification.md&quot;&gt;Binary Classification&lt;/a&gt; model for determining whether a picture contains a hot dog or not. If your model has 1 TP and 1 FP, you get the precision metric equal to 0.5. This means that if your model predicts that some photo contains a hot dog, it is correct 50% of the time.&lt;/p&gt;
&lt;p&gt;If your model has no FPs, it will result in 100% precision. You can easily &quot;cheat&quot; this by making your model always return negatives. This means that your recall will go down.&lt;/p&gt;
&lt;h1&gt;Recall&lt;/h1&gt;
&lt;p&gt;Recall measures how many of the &lt;strong&gt;actual positives&lt;/strong&gt; were identified correctly.
$$
Recall = \frac{TP}{TP+FN}
$$
Continuing with the precision example, If your model has 2 TP and 3 FN, you get a recall equal to 0.4. This means that of all pictures (e.g. 10, with 5 of them being hot dogs), your model will identify 40% &lt;em&gt;of the hot dogs&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;If your model has no FNs, it will result in 100% recall. You can easily &quot;cheat&quot; this by making your model always return positives. This means that your precision will go down.&lt;/p&gt;
&lt;h1&gt;When to use one over the other&lt;/h1&gt;
&lt;p&gt;Both can be useful, depending on the context you&apos;re using them in. A simple mnemonic to remember the differences is &quot;&lt;strong&gt;PRE&lt;/strong&gt;cision is to &lt;strong&gt;PRE&lt;/strong&gt;gnancy tests as re&lt;strong&gt;CALL&lt;/strong&gt; is to &lt;strong&gt;CALL&lt;/strong&gt; center&quot;&lt;/p&gt;
&lt;p&gt;In a pregnancy test, you&apos;d want to have high precision, i.e. reducing FPs. You don&apos;t want to give people false-hopes by making them think they are pregnant. People might end up making rash decisions based on the result. FNs on the other hand aren&apos;t as bad, as you&apos;ll find that you&apos;re pregnant later on anyway.&lt;/p&gt;
&lt;p&gt;In a call center for insurance claims, you&apos;d want to have high recall, i.e. reducing FNs. If the employees&apos; job is to mark claims as a scam (to be further investigated) or not a scam (to pay money to the claimant directly), they&apos;d want to play on the safe side and report anything that seems likely to be a fraud. This will result in more FPs (meaning that employees falsely reported claims as scams, when they were actually legitimate), as opposed to letting a scam fall through the cracks (yielding more FNs)&lt;/p&gt;
&lt;h1&gt;Terminology&lt;/h1&gt;
&lt;p&gt;TP means True Positive
FP means False Positive
TN means True Negative
FN means False Negative&lt;/p&gt;
&lt;h1&gt;References&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;https://developers.google.com/machinelearning/crashcourse/classification/precisionandrecall&lt;/li&gt;
&lt;li&gt;https://www.wikiwand.com/en/Precision_and_recall&lt;/li&gt;
&lt;li&gt;https://datascience.stackexchange.com/questions/30881/whenisprecisionmoreimportantoverrecall&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Prefix Property</title><link>https://simo.sh/study/prefix-property?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/study/prefix-property?utm_source=rss</guid><pubDate>Thu, 18 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The prefix property allows you to encode different symbols of data with variable-length without using any special markers in between. Any encoding algorithm that can represent different symbols without using common prefixes follows this property.
Fixed-length codes inherently abide by this property.&lt;/p&gt;
&lt;h1&gt;Example&lt;/h1&gt;
&lt;p&gt;An example of an encoding that &lt;em&gt;doesn&apos;t&lt;/em&gt; have the prefix property is this:
&lt;img src=&quot;../../assets/Drawing-2023-11-04-17.58.35.svg&quot; alt=&quot;Drawing 2023-11-04 17.58.35.excalidraw&quot; /&gt;
You can see that there is ambiguity when decoding - the encoding for A prefixes the encoding for B.
An example of an encoding that &lt;em&gt;does&lt;/em&gt; have the prefix property is this:
&lt;img src=&quot;../../assets/Drawing-2023-11-04-18.10.54.svg&quot; alt=&quot;Drawing 2023-11-04 18.10.54.excalidraw&quot; /&gt;
There is no ambiguity when decoding&lt;/p&gt;
&lt;h1&gt;References&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;https://www.wikiwand.com/en/Prefix_code&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Supervised Learning</title><link>https://simo.sh/study/supervised-learning?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/study/supervised-learning?utm_source=rss</guid><pubDate>Thu, 18 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Supervised learning is an approach for machine learning where your model goes over thousands of labeled entries in some data set.&lt;/p&gt;
&lt;p&gt;For every input of the data set, your model returns an output. You use both the input and the output, combined with an &lt;a href=&quot;./Optimizer.md&quot;&gt;Optimizer&lt;/a&gt; to adjust the model&apos;s parameters.&lt;/p&gt;
&lt;p&gt;You repeat this process many times. One iteration over the data set is called an epoch.&lt;/p&gt;
&lt;p&gt;You&apos;re supposed to split and randomize your dataset in at least 2 parts - a training one and a testing one.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You use the training one for the actual learning process – adjusting your parameters.&lt;/li&gt;
&lt;li&gt;You then use the testing one to validate / evaluate the accuracy of the model, by giving it data that it&apos;s never seen before. You don&apos;t adjust the parameters when going over the testing dataset.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Transformer Architecture</title><link>https://simo.sh/study/transformer-architecture?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/study/transformer-architecture?utm_source=rss</guid><pubDate>Thu, 18 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This is a &lt;a href=&quot;./Neural-Network.md&quot;&gt;Neural Network&lt;/a&gt; architecture which originates from the paper &quot;&lt;a href=&quot;https://arxiv.org/pdf/1706.03762.pdf&quot;&gt;Attention Is All You Need&lt;/a&gt;&quot;. It&apos;s primarily use is for text based models, but can also be modified to be used for other data sources, e.g. image purposes (&lt;a href=&quot;Vision%20Transformer.md&quot;&gt;Vision Transformer&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;The transformer model can learn which words are important to a given sentence (as well as how words relate to one another), using the attention mechanism. Unlike previous NLP models, it can have an infinitely long context window (within the provided input limit).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/Pasted-image-20231206204234.png&quot; alt=&quot;641&quot; /&gt;&lt;/p&gt;
&lt;p&gt;It consists of 2 main components – the Encoder (left) and the Decoder (right).
Both of them are provided with a text input which is &lt;a href=&quot;./Word-Tokenizing.md&quot;&gt;tokenized&lt;/a&gt; and then represented as a &lt;a href=&quot;./Vector-Embedding.md&quot;&gt;Vector Embedding&lt;/a&gt;.  One of the goals is optimizing the encoder and decoder, such that we get the best representation of our inputs. This is known as pre-training. After we&apos;ve pretrained the encoder and decoder, we can change the head at the end and fine-tune our model for different purposes.&lt;/p&gt;
&lt;p&gt;The Encoder&apos;s purpose is to encode the meaning of the input (the vector embedding), the positions of the words themselves (&lt;a href=&quot;./Positional-Encoding.md&quot;&gt;Positional Encoding&lt;/a&gt;), as well as how &lt;strong&gt;words&lt;/strong&gt; relate to one another (&lt;a href=&quot;Multi-Head%20Attention.md&quot;&gt;Multi-Head Attention&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;The Decoder&apos;s purpose is exactly the same, but for the output input. The masked multi-head attention head prevents the model from &quot;cheating&quot; by looking ahead in the output input - it assigns $-\infty$ to the values above the main diagonal. This will make the model causal - it only depends on previous states.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/Pasted-image-20231209140741.png&quot; alt=&quot;Pasted image 20231209140741.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The encoder&apos;s output is connected to the decoder&apos;s second multi-head attention layer - acting as the Key and Value inputs. We combine that with the output of the previous masked multi-head attention layer to act as the Query input.&lt;/p&gt;
&lt;p&gt;In between all the layers, you can find &lt;a href=&quot;Layer%20Normalization.md&quot;&gt;Layer Normalization&lt;/a&gt; layers to normalize the values.&lt;/p&gt;
&lt;p&gt;During training, the input and output embeddings represent the input and the &lt;em&gt;expected&lt;/em&gt; output. You surround the inputs with special tokens to indicate the beginning / end of sentences. The model outputs &lt;em&gt;all&lt;/em&gt; of the next tokens in 1 time step. There&apos;s no for loop or iteration required for the model to give an answer.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/Pasted-image-20231207012847.png&quot; alt=&quot;Pasted image 20231207012847.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;During inference, the input is the also surrounded with special tokens, but the output input is &lt;em&gt;only&lt;/em&gt; a token which indicates the beginning of a sentence. This will spit out a single token and you have to keep feeding the output of the model to the output input. You repeat this until you reach a special end of sentence token.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/Pasted-image-20231207012911.png&quot; alt=&quot;Pasted image 20231207012911.png&quot; /&gt;&lt;/p&gt;
&lt;h1&gt;References&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/bCz4OMemCcA&quot;&gt;https://youtu.be/bCz4OMemCcA&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Vector Embedding</title><link>https://simo.sh/study/vector-embedding?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/study/vector-embedding?utm_source=rss</guid><pubDate>Thu, 18 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;You can represent any kind of data as an N-dimensional vector. For example you can use it for representing words when doing natural language processing. You do to this by assigning a random vector to each word in your input. You then train some kind of model, such that it begins grouping related words closer together and opposite words further from each other. The dimensionality of the vector is completely arbitrary and it&apos;s up to you to decide. You would &lt;em&gt;hope&lt;/em&gt; that the different parts of the vector represent or learn different aspects of the word – but you can&apos;t really know what the numbers mean for sure as it&apos;s all a black box.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/Pasted-image-20231206205059.png&quot; alt=&quot;Pasted image 20231206205059.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;These vectors can be used to perform different mathematical operations. For example, if you take the vector for &quot;King&quot;, subtract the vector for &quot;Man&quot;, add the vector for &quot;Woman&quot;, you would get a vector that&apos;s &lt;em&gt;very close&lt;/em&gt; to the vector for &quot;Queen&quot;.
The same idea can be applied to other form of data, for example images.&lt;/p&gt;
&lt;h1&gt;References&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;https://www.tensorflow.org/text/guide/word_embeddings&lt;/li&gt;
&lt;li&gt;https://youtu.be/gQddtTdmG_8&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Word Tokenizing</title><link>https://simo.sh/study/word-tokenizing?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/study/word-tokenizing?utm_source=rss</guid><pubDate>Thu, 18 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Words in their raw string form can&apos;t be used as as inputs to machine learning models. Tokenizing is the process of assigning a unique number id for each word in your data set / vocabulary. You can build such a vocabulary by going over your data set and assigning random values to each unique word. You may reserve some of these values as special tokens, e.g.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Marking the beginning or the end of your input&lt;/li&gt;
&lt;li&gt;Distinguishing questions from answers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Given that your input will be of varying length, you have to pad your input with zeroes, such that it matches the length of your expected input size.&lt;/p&gt;
&lt;h1&gt;References&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;https://www.youtube.com/watch?v=bWLvGGJLzF8&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Open Graph Previews using Figma and Satori</title><link>https://simo.sh/blog/open-graph-previews-using-figma-and-satori?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/blog/open-graph-previews-using-figma-and-satori?utm_source=rss</guid><description>Dynamically building Open Graph thumbnails using Satori</description><pubDate>Sun, 02 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Intro&lt;/h1&gt;
&lt;p&gt;When you post a link on Discord, Telegram, Twitter, etc, their servers use metadata that&apos;s defined on the page to generate a preview for your link:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/OpenGraph-Previews-using-Figma-and-Satori-20240602180322414.webp&quot; alt=&quot;OpenGraph Previews using Figma and Satori-20240602180322414.webp&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This is &lt;em&gt;mainly&lt;/em&gt; powered by &lt;a href=&quot;https://ogp.me/&quot;&gt;Open Graph&lt;/a&gt;, which is a protocol made by Facebook that defines some special meta tags that are used by social networks/messaging apps.&lt;/p&gt;
&lt;p&gt;I was looking for a way to dynamically generate these images at build time for my blog posts and came across &lt;a href=&quot;https://dietcode.io/p/astro-og/&quot;&gt;this&lt;/a&gt; and &lt;a href=&quot;https://www.kozhuhds.com/blog/generating-static-open-graph-og-images-in-astro-using-vercel-og/#step-4-adding-og-tags-in-html&quot;&gt;this&lt;/a&gt; article, which helped in my implementation.&lt;/p&gt;
&lt;h1&gt;Designing in Figma&lt;/h1&gt;
&lt;p&gt;I started by making some designs in &lt;a href=&quot;https://www.figma.com/&quot;&gt;Figma&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/Open-Graph-Previews-using-Figma-and-Satori-20240602181759817.webp&quot; alt=&quot;Open Graph Previews using Figma and Satori-20240602181759817.webp&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Instead of manually reimplementing the one I wanted using HTML &amp;amp; CSS, I decided to export it to SVG. To do that, I used the &lt;a href=&quot;https://www.figma.com/community/plugin/814345141907543603/svg-export&quot;&gt;SVG Export&lt;/a&gt; Figma plugin.
Before doing that, I hid the text layers, as they&apos;ll be interpolated in my Satori template.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/Open-Graph-Previews-using-Figma-and-Satori-20240602182426365.webp&quot; alt=&quot;Open Graph Previews using Figma and Satori-20240602182426365.webp&quot; /&gt;&lt;/p&gt;
&lt;h1&gt;Satori Rendering&lt;/h1&gt;
&lt;p&gt;I then copied the generated SVG code into Vercel&apos;s &lt;a href=&quot;https://og-playground.vercel.app/&quot;&gt;OG Playground&lt;/a&gt; which uses &lt;a href=&quot;https://github.com/vercel/satori&quot;&gt;Satori&lt;/a&gt; under the hood. Satori is a JS library that converts HTML / CSS to SVG. I used the playground to verify my experiments in real-time as it updates the preview as soon as you change the code on the left.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/Open-Graph-Previews-using-Figma-and-Satori-20240602183930528.webp&quot; alt=&quot;Open Graph Previews using Figma and Satori-20240602183930528.webp&quot; /&gt;
In the OG Playground, I added the &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; tags for the post title and subtitle. I also made sure to set the container width/height to 1200x630 as that&apos;s the recommended Open Graph preview size. The fonts slightly mismatch with the original Figma design, but that&apos;s because DM Sans (the font I use) isn&apos;t imported in the playground.&lt;/p&gt;
&lt;p&gt;The OG Playground (and Satori) uses JSX or React-elements-like syntax&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import satori from &apos;satori&apos;

const svg = await satori(
  &amp;lt;div style={{ color: &apos;black&apos; }}&amp;gt;hello, world&amp;lt;/div&amp;gt;,
  {
    width: 600,
    height: 400,
    fonts: [
      {
        name: &apos;Roboto&apos;,
        data: robotoArrayBuffer,
        weight: 400,
        style: &apos;normal&apos;,
      },
    ],
  },
)

// or

await satori(
  {
    type: &apos;div&apos;,
    props: {
      children: &apos;hello, world&apos;,
      style: { color: &apos;black&apos; },
    },
  },
  options
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Astro doesn&apos;t support JSX out of the box and requires extra configuration to allow that. Instead of manually building the React-elements-like tree, I used &lt;a href=&quot;https://github.com/natemoo-re/satori-html&quot;&gt;satori-html&lt;/a&gt;. This library takes in raw HTML as a string and converts it to the aforementioned tree.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { html } from &quot;satori-html&quot;   
  
export const buildHTML = (  
  title: string,  
  date: Date,  
  slug: string,  
) =&amp;gt; {  
	return html`&amp;lt;div style=&quot;...&quot;&amp;gt;
      &amp;lt;svg
        xmlns=&quot;http://www.w3.org/2000/svg&quot;
        width=&quot;1200&quot;
        height=&quot;630&quot;
        fill=&quot;none&quot;
        viewBox=&quot;0 0 1200 630&quot;
      &amp;gt;
        ...
      &amp;lt;/svg&amp;gt;
      &amp;lt;p style=&quot;...&quot;&amp;gt;${title}&amp;lt;/p&amp;gt;
      &amp;lt;p style=&quot;...&quot;&amp;gt;
        -rw-r--r-- 1 simo nerv 1337
        ${date.toLocaleDateString(&quot;en-US&quot;, {
          year: &quot;numeric&quot;,
          month: &quot;long&quot;,
          day: &quot;numeric&quot;,
        })}
        ${slug}.md
      &amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
	`
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Static generation using Astro Endpoints&lt;/h1&gt;
&lt;p&gt;I then created an &lt;a href=&quot;https://docs.astro.build/en/guides/endpoints/&quot;&gt;Astro Endpoint&lt;/a&gt; called &lt;code&gt;og.png.ts&lt;/code&gt; for each of my collections.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/Open-Graph-Previews-using-Figma-and-Satori-20240602190404988.webp&quot; alt=&quot;Open Graph Previews using Figma and Satori-20240602190404988.webp&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { type CollectionEntry, getCollection } from &quot;astro:content&quot;  
import { buildHTML, buildOG } from &quot;../../../og.ts&quot;  
import { filenameToTitle } from &quot;../../../filenameToTitle.ts&quot;  
  
interface Props {  
  params: { slug: string }  
  props: { post: CollectionEntry&amp;lt;&quot;blog&quot;&amp;gt; }  
}  
  
export async function GET({ props }: Props) {  
  const { post } = props
  const buffer = await buildOG(  
    buildHTML(filenameToTitle(post.id), post.data.date, &quot;blog&quot;, post.slug),  
  )  
  return new Response(buffer, {  
    headers: { &quot;Content-Type&quot;: &quot;image/png&quot; },  
  })
}  

// Invoke the /blog/[slug]/og.png.ts endpoint for each blog post
export async function getStaticPaths() {  
  const posts = await getCollection(&quot;blog&quot;)  
  return posts.map((post) =&amp;gt; ({  
    params: { slug: post.slug },  
    props: { post: post },  
  }))
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;buildOG&lt;/code&gt; function takes in the generated tree from satori-html and returns a PNG preview of the post. To convert the SVG to PNG, I used &lt;a href=&quot;https://github.com/yisibl/resvg-js&quot;&gt;resvg-js&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { readFile } from &quot;fs/promises&quot;  
import satori from &quot;satori&quot;  
import { Resvg } from &quot;@resvg/resvg-js&quot;
// ...
const dmSans = await readFile(&quot;./public/fonts/DM-Sans.ttf&quot;)
export const buildOG = async (input: ReturnType&amp;lt;typeof html&amp;gt;) =&amp;gt; {
  const svg = await satori(input, {
    width: 1200,
    height: 630,
    fonts: [
      {
        name: &quot;DM Sans&quot;,
        data: dmSans,
        weight: 700,
        style: &quot;normal&quot;,
      },
    ],
  })
  const resvg = new Resvg(svg)
  return resvg.render().asPng()
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Astro will then go over every post in each collection using the &lt;code&gt;getStaticPaths&lt;/code&gt; function and render a file called &lt;code&gt;og.png&lt;/code&gt; in the respective &lt;code&gt;/${collection}/[slug]/&lt;/code&gt; directory.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ tree ./dist/projects
.
├── discord-friends
│   ├── index.html
│   └── og.png
├── findaudit
│   ├── index.html
│   └── og.png
├── flappy-ai
│   ├── index.html
│   └── og.png
├── index.html
├── piral
│   ├── index.html
│   └── og.png
├── push
│   ├── index.html
│   └── og.png
├── sugoku
│   ├── index.html
│   └── og.png
└── tic-tac-toe-solver
    ├── index.html
    └── og.png

8 directories, 15 files
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then this &lt;code&gt;Metadata.astro&lt;/code&gt; component  adds the necessary &lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt; tags:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
interface Props {  
  title: string
  type: &quot;website&quot; | &quot;article&quot;  
  publishedTime?: string
  canonicalUrl: string
  description: string
  image: string
}  
  
const { title, description, image, canonicalUrl, type, publishedTime } = Astro.props  
---  
&amp;lt;link rel=&quot;canonical&quot; href={canonicalUrl} /&amp;gt;  
&amp;lt;meta property=&quot;og:title&quot; content={title} /&amp;gt;  
&amp;lt;meta property=&quot;og:description&quot; content={description} /&amp;gt;  
&amp;lt;meta property=&quot;og:url&quot; content={canonicalUrl} /&amp;gt;  
&amp;lt;meta property=&quot;og:image&quot; content={image} /&amp;gt;  
&amp;lt;meta property=&quot;og:type&quot; content={type} /&amp;gt;  
&amp;lt;meta name=&quot;twitter:card&quot; content=&quot;summary_large_image&quot; /&amp;gt;  
&amp;lt;meta name=&quot;twitter:title&quot; content={title} /&amp;gt;  
&amp;lt;meta property=&quot;twitter:description&quot; content={description} /&amp;gt;  
&amp;lt;meta name=&quot;twitter:image&quot; content={image} /&amp;gt;
&amp;lt;meta name=&quot;description&quot; content={description} /&amp;gt;  
{publishedTime &amp;amp;&amp;amp; &amp;lt;meta property=&quot;article:published_time&quot; content={publishedTime} /&amp;gt;} 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In my &lt;code&gt;/blog/[slug]/index.astro&lt;/code&gt; file (which statically builds a page for each slug/entry), I render the &lt;code&gt;Metadata.astro&lt;/code&gt; component:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;...
&amp;lt;Blog filename={entry.id} {...entry.data}&amp;gt;
  &amp;lt;Metadata
    slot=&quot;head&quot;
    title={filenameToTitle(entry.id)}
    description={entry.data.excerpt}
    image={`${Astro.site}blog/${entry.slug}/og.png`}
    canonicalUrl={`${Astro.site}blog/${entry.slug}`}
    publishedTime={entry.data.date.toISOString()}
    type=&quot;article&quot;
  /&amp;gt;
  &amp;lt;Content /&amp;gt;
&amp;lt;/Blog&amp;gt;
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As each thumbnail is called &lt;code&gt;og.png&lt;/code&gt; in the same directory as the html page, referring to the&lt;code&gt;${Astro.site}/blog/[slug]/og.png&lt;/code&gt; image, will result in the following &lt;code&gt;meta&lt;/code&gt; tag:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;meta property=&quot;og:image&quot; content=&quot;https://simo.sh/blog/uses/og.png&quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The final result looks like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/Open-Graph-Previews-using-Figma-and-Satori-20240602201005600.webp&quot; alt=&quot;Open Graph Previews using Figma and Satori-20240602201005600.webp&quot; /&gt;&lt;/p&gt;
&lt;h1&gt;References&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;https://og-playground.vercel.app/&lt;/li&gt;
&lt;li&gt;https://www.kozhuhds.com/blog/generating-static-open-graph-og-images-in-astro-using-vercel-og/&lt;/li&gt;
&lt;li&gt;https://dietcode.io/p/astro-og/&lt;/li&gt;
&lt;li&gt;https://github.com/vercel/satori&lt;/li&gt;
&lt;li&gt;https://github.com/natemoo-re/satori-html&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Flushing DNS on macOS</title><link>https://simo.sh/notes/flushing-dns-on-macos?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/notes/flushing-dns-on-macos?utm_source=rss</guid><description>Short one liner for flushing your DNS on macOS</description><pubDate>Wed, 29 May 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;here&apos;s a one liner that flushes your DNS cache on macOS&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>My Gear</title><link>https://simo.sh/blog/uses?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/blog/uses?utm_source=rss</guid><description>A list of all the hardware and software that I use</description><pubDate>Tue, 28 May 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;../../assets/macos.webp&quot; alt=&quot;macos.webp&quot; /&gt;&lt;/p&gt;
&lt;h1&gt;Desktop PC&lt;/h1&gt;
&lt;p&gt;I use this machine for gaming.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CPU&lt;/td&gt;
&lt;td&gt;Ryzen 5 5600X&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPU&lt;/td&gt;
&lt;td&gt;GeForce RTX 3070&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RAM&lt;/td&gt;
&lt;td&gt;Crucial Ballistix 2x8GB 3600MHz, G.SKILL Trident Z 2x8GB 3600MHz&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Motherboard&lt;/td&gt;
&lt;td&gt;MSI MAG B550 Tomahawk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU Cooler&lt;/td&gt;
&lt;td&gt;be quiet! Pure Rock Cooler&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSD&lt;/td&gt;
&lt;td&gt;Samsung 970 Evo 250GB, Kingston KC3000 2TB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HDD&lt;/td&gt;
&lt;td&gt;Toshiba P300 1TB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PSU&lt;/td&gt;
&lt;td&gt;BITFENIX Whisper M 650W&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Case&lt;/td&gt;
&lt;td&gt;Cooler Master Force 500&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h1&gt;Monitors&lt;/h1&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Brand&lt;/th&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;LG&lt;/td&gt;
&lt;td&gt;27GP850&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BenQ&lt;/td&gt;
&lt;td&gt;GL2580H x2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;These are all attached to an Arctic Z3 Pro Gen 3 stand&lt;/p&gt;
&lt;h1&gt;VR&lt;/h1&gt;
&lt;p&gt;I have an Oculus Quest 3 256GB with the &lt;a href=&quot;https://www.bobovr.com/collections/quest-3-accessories/products/s3-pro&quot;&gt;BOBOVR S3 Pro Super Strap&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Keyboards&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://youtu.be/lM2Gf-WhwXI&quot;&gt;I use a QWERTY keyboard&lt;/a&gt;.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Brand&lt;/th&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Switches&lt;/th&gt;
&lt;th&gt;Using?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;HHKB&lt;/td&gt;
&lt;td&gt;Hybrid Type-S&lt;/td&gt;
&lt;td&gt;Topre&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Varmilo&lt;/td&gt;
&lt;td&gt;VA68&lt;/td&gt;
&lt;td&gt;MX Brown&lt;/td&gt;
&lt;td&gt;Rarely&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Logitech&lt;/td&gt;
&lt;td&gt;G610&lt;/td&gt;
&lt;td&gt;MX Red&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Mice&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Brand&lt;/th&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Using?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Logitech&lt;/td&gt;
&lt;td&gt;G Pro X Superlight&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Logitech&lt;/td&gt;
&lt;td&gt;G403&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Logitech&lt;/td&gt;
&lt;td&gt;G102&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;I use the Zowie G-SR mousepad for all of them.&lt;/p&gt;
&lt;h1&gt;Chair&lt;/h1&gt;
&lt;p&gt;I&apos;ve been using the Herman Miller Aeron Remastered Graphite since 2022.&lt;/p&gt;
&lt;h1&gt;Audio&lt;/h1&gt;
&lt;p&gt;I have a pair of Audio-Technica ATH-M50X w/ a FiiO E10K Olympus 2 but never use them nowadays as their head clamp is way too tight for me. Instead, I have a pair of Audioengine A2+ speakers that I use every day. My microphone is a HyperX SoloCast, mounted on a cheap arm.&lt;/p&gt;
&lt;h1&gt;Hubs&lt;/h1&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Brand&lt;/th&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Dell&lt;/td&gt;
&lt;td&gt;D6000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Anker&lt;/td&gt;
&lt;td&gt;5-in-1 USB C Hub&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UGREEN&lt;/td&gt;
&lt;td&gt;USB Switch 4 in 2 Out&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h1&gt;Work&lt;/h1&gt;
&lt;p&gt;&lt;s&gt;I&apos;ve been using a MacBook Pro 13&quot; M1 16GB 1TB since 2021. I&apos;ve been very satisfied with its battery life and performance.&lt;/s&gt;
Recently I got a MacBook Pro 14&quot; M3 Max (16 Core CPU, 40 Core GPU, 48GB RAM, 1TB SSD) after dealing with many random crashes, touch bar issues, (and generally DisplayLink problems) for the last 6+ months with the old one.  ^5ba2ac&lt;/p&gt;
&lt;p&gt;For my main development, I use JetBrains&apos; tools - WebStorm, GoLand, etc. I used to be a die-hard Visual Studio Code fan for years but in 2021 I decided to change things up a bit and haven&apos;t looked back.&lt;/p&gt;
&lt;p&gt;For my terminal I use &lt;a href=&quot;https://iterm2.com/&quot;&gt;iTerm2&lt;/a&gt;. Instead of using its built-in tabs, I use tmux as I&apos;m used to its bindings and behavior after using it since 2019. There are other reasons why I love tmux, for which I&apos;m planning on writing a blog post.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/tmux-and-neovim.webp&quot; alt=&quot;tmux-and-neovim.webp&quot; /&gt;&lt;/p&gt;
&lt;p&gt;For editing within my terminal, I use neovim. I use fish as my preferred interactive shell instead of zsh / bash due to the (in my opinion) better out-of-the-box experience. I&apos;ve barely configured it and love all the built in features.&lt;/p&gt;
&lt;p&gt;I&apos;m a huge fan of &lt;a href=&quot;https://www.raycast.com/&quot;&gt;Raycast&lt;/a&gt; and have been using it for a few years now. It&apos;s miles better than Spotlight. There are so many features that I love about it that I can&apos;t list them all here.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/raycast.webp&quot; alt=&quot;raycast.webp&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Some other tools I use include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://orbstack.dev/&quot;&gt;Orbstack&lt;/a&gt; – an amazing alternative to Docker Desktop &amp;amp; Rancher Desktop&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arc.net/&quot;&gt;Arc&lt;/a&gt; – hipster browser that I&apos;ve been using since August 2022. I&apos;m a huge fan of the way it handles pinned tabs and Spaces/Profiles.&lt;/li&gt;
&lt;li&gt;&lt;s&gt;&lt;a href=&quot;https://amie.so/&quot;&gt;Amie&lt;/a&gt; – overrated calendar app that looks better than the built in Apple one.&lt;/s&gt; went back to the built in Apple one. Amie is super slow for me&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://obsidian.md/&quot;&gt;Obsidian&lt;/a&gt; – I use it for general note taking, journaling, bookmarking, as well as this blog&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/jordanbaird/Ice&quot;&gt;Ice&lt;/a&gt; - simple app that allows you to hide icons from your macOS menu bar&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://apphousekitchen.com/&quot;&gt;AlDente&lt;/a&gt; – battery management tool for MacBooks&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://shottr.cc/&quot;&gt;Shottr&lt;/a&gt; – screenshot utility&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tailscale.com/&quot;&gt;Tailscale&lt;/a&gt; – based VPN for my devices – phone, laptop, servers, desktop, IoT.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.reederapp.com/&quot;&gt;Reeder&lt;/a&gt; – RSS client for macOS &amp;amp; iOS. It&apos;s wired up to a &lt;a href=&quot;https://freshrss.org/index.html&quot;&gt;FreshRSS&lt;/a&gt; instance on my home cluster&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://linearmouse.app/&quot;&gt;LinearMouse&lt;/a&gt; – must have app for disabling mouse acceleration / adjusting scroll speed &amp;amp; direction on a per device basis, etc&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://crystalidea.com/macs-fan-control&quot;&gt;Macs Fan Control&lt;/a&gt; – self explanatory&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.screen.studio/&quot;&gt;Screen Studio&lt;/a&gt; – screen recorder &amp;amp; editor which allows you to make fancy demo videos (please don&apos;t overuse the zoom effect like 95% of so called &quot;indie hackers&quot; on Twitter do)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://firecore.com/infuse&quot;&gt;Infuse&lt;/a&gt; – alternative &lt;a href=&quot;https://www.plex.tv/&quot;&gt;Plex&lt;/a&gt; (and media in general) client for macOS / iOS / tvOS&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cyberduck.io/&quot;&gt;CyberDuck&lt;/a&gt; – all-in-one storage browser, from S3, to FTP, to WebDAV, etc.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://highlyopinionated.co/swish/&quot;&gt;Swish&lt;/a&gt; – macOS window management tool. Mainly targeted towards trackpad use, but ironically I rarely use those gestures. Great alternative would be &lt;a href=&quot;https://rectangleapp.com/&quot;&gt;Rectangle&lt;/a&gt; (or whenever macOS Sequoia comes out)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://proxyman.io/&quot;&gt;Proxyman&lt;/a&gt; – HTTP traffic interceptor for macOS &amp;amp; iOS. Similar to &lt;a href=&quot;https://www.wireshark.org/&quot;&gt;Wireshark&lt;/a&gt;, but more intuitive.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://culturedcode.com/things/&quot;&gt;Things&lt;/a&gt; – simple TODO app, have been trying it out for the last few months.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.parallels.com/&quot;&gt;Parallels&lt;/a&gt; – VM hypervisor, has some neat things&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://rogueamoeba.com/loopback/&quot;&gt;Loopback&lt;/a&gt; – audio channel router/mixer&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://betterdisplay.pro/&quot;&gt;BetterDisplay&lt;/a&gt; – custom resolution tool&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://lowtechguys.com/clop/&quot;&gt;Clop&lt;/a&gt; – really neat compression utility&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jomo.so/&quot;&gt;Jomo&lt;/a&gt; – app &amp;amp; website blocker/limiter&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Discord Friends</title><link>https://simo.sh/projects/discord-friends?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/projects/discord-friends?utm_source=rss</guid><description>Visualising your Discord&apos;s relationship graph in 3D space</description><pubDate>Mon, 01 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/videos/discord-mutual-friends.mp4&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;2 years ago, this started off as a simple script that generated a &lt;a href=&quot;https://graphviz.org/doc/info/lang.html&quot;&gt;GraphViz DOT&lt;/a&gt; file which could then be visualised with a tool like &lt;a href=&quot;https://gephi.org/&quot;&gt;Gephi&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/Discord-Friends-20240530135444315.webp&quot; alt=&quot;Discord Friends-20240530135444315.webp&quot; /&gt;&lt;/p&gt;
&lt;p&gt;2 years later I decided to turn it into a proper desktop app, using &lt;a href=&quot;https://wails.io/&quot;&gt;Wails&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;After hacking it together in a few days, I published it on &lt;a href=&quot;https://github.com/fr3fou/discord-mutual-friends&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>FindAudit</title><link>https://simo.sh/projects/findaudit?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/projects/findaudit?utm_source=rss</guid><description>Connecting cybersecurity researchers with companies seeking security audits</description><pubDate>Fri, 01 Mar 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;FindAudit connects cybersecurity researchers with companies seeking security audits. You can find out more about it &lt;a href=&quot;https://www.findaudit.xyz/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>Push</title><link>https://simo.sh/projects/push?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/projects/push?utm_source=rss</guid><description>Social network marketplace for buying and selling fashion products</description><pubDate>Sat, 16 Apr 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/videos/push.webm&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Push Marketplace is a social network for buying and selling fashion items. We&apos;ve helped 100k+ users connect with like-minded fashion lovers. Together we&apos;ve worked with many celebrities and influencers, including, but not exclusive to, FYRE, Emil TRF, V:RGO, Viktor Stoyanov, and many more.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;In mid-2021, my two co-founders and I started working on Push. We developed a minimum viable product (MVP) and released it to the app stores on April 16th, 2022. Within the first 24 hours, we gained over &lt;strong&gt;1000&lt;/strong&gt; users, processed &lt;strong&gt;25&lt;/strong&gt; orders, and had over &lt;strong&gt;750&lt;/strong&gt; listings. Our app led the Apple App Store charts as the most downloaded app for several days.&lt;/p&gt;
&lt;p&gt;Since then, we&apos;ve secured funding from angel investments and two venture capitalist firms, Vitosha Venture Partners and Innovation Capital.&lt;/p&gt;
&lt;p&gt;As of the time of writing this, we&apos;ve partnered with &lt;strong&gt;25+&lt;/strong&gt; influencers and celebrities, processed over &lt;strong&gt;15k&lt;/strong&gt; orders, and have more than &lt;strong&gt;20k&lt;/strong&gt; active listings. We&apos;re currently operating only in the Bulgarian region, but we&apos;re planning on expanding to other countries in Eastern Europe.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;My technical role in building Push spans the entire stack – from the mobile app to the backend server, all the way to the infrastructure it&apos;s deployed on. The mobile app uses React and Capacitor.js, allowing us to share one codebase for Android and iOS. For the backend, we use Go, Postgres, Redis, and MeiliSearch. All of this is deployed on a GKE Kubernetes cluster and monitored using Prometheus, Loki, and Grafana. The infrastructure is entirely provisioned using Terraform. Here I&apos;ll be blogging about some of the technical lessons I&apos;ve learned while building this startup.&lt;/p&gt;
</content:encoded></item><item><title>Watching a PostgreSQL Query</title><link>https://simo.sh/notes/watching-a-postgresql-query?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/notes/watching-a-postgresql-query?utm_source=rss</guid><description>Small script for watching a Postgres query</description><pubDate>Thu, 22 Jul 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;few years ago, I had to debug a very hard-to-reproduce bug with my production backend deployment. for some reason db writes were coming in late or not coming in at all. in order to monitor the state of my database, i created a small script that runs my query every &lt;code&gt;n&lt;/code&gt; seconds.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env bash
if [ &quot;$1&quot; == &quot;help&quot; ] || [ $# -eq 0 ]; then
    echo &quot;usage: watch-psql &apos;select count(*) from users&apos; 0.1 123 app&quot;;
    echo &quot;                               ^                ^   ^   ^ &quot;;
    echo &quot;                             query            time pass db&quot;;
    exit 0;
fi

if [ -z &quot;$1&quot; ]; then
    echo &quot;No query provided&quot;;
    exit 1;
fi

if [ -z &quot;$2&quot; ]; then
    echo &quot;No time provided&quot;;
    exit 1;
fi

if [ -z &quot;$3&quot; ]; then
    echo &quot;No password provided&quot;;
    exit 1;
fi

if [ -z &quot;$4&quot; ]; then
    echo &quot;No db provided&quot;;
    exit 1;
fi

PGPASSWORD=$3 watch -n $2 &quot;echo \&quot;$1\&quot; | psql -h localhost -U postgres -d $4&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;the code is kinda crappy but it helped. nowadays i&apos;d use datagrip&apos;s built in refresh feature&lt;/p&gt;
</content:encoded></item><item><title>Piral</title><link>https://simo.sh/projects/piral?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/projects/piral?utm_source=rss</guid><description>Visualisation of an Archimedean spiral using prime numbers</description><pubDate>Thu, 19 Nov 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/videos/Piral.mp4&quot; alt=&quot;autoplay&quot; /&gt;This was inspired by 3Blue1Brown&apos;s video &lt;a href=&quot;https://www.youtube.com/watch?v=EK32jo7i5LQ&quot;&gt;on the same topic&lt;/a&gt;. The source code is &lt;a href=&quot;https://github.com/fr3fou/piral&quot;&gt;here&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Tic Tac Toe Solver</title><link>https://simo.sh/projects/tic-tac-toe-solver?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/projects/tic-tac-toe-solver?utm_source=rss</guid><description>Tic-Tac-Toe implementation &amp; solver using minimax &amp; alpha-beta pruning</description><pubDate>Wed, 12 Aug 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This is a tic-tac-toe implementation in Go &amp;amp; Raylib, as well as a minimax &amp;amp; alpha-beta pruning solver. The solver will always play the most optimal turn, making it impossible to beat.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/tic-tac-toe-win.gif&quot; alt=&quot;tic-tac-toe-win.gif&quot; /&gt;&lt;/p&gt;
&lt;p&gt;At best, you can draw it:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/tic-tac-toe-draw.gif&quot; alt=&quot;tic-tac-toe-draw.gif&quot; /&gt;&lt;/p&gt;
&lt;p&gt;You can check the source code &lt;a href=&quot;https://github.com/fr3fou/tic-tac-toe-solver&quot;&gt;here&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Flappy AI</title><link>https://simo.sh/projects/flappy-ai?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/projects/flappy-ai?utm_source=rss</guid><description>Neuroevolution genetic algorithm for playing Flappy Bird</description><pubDate>Sun, 26 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A genetic algorithm with a neural network (&lt;a href=&quot;https://github.com/fr3fou/gone&quot;&gt;fr3fou/gone&lt;/a&gt;) built on top of &lt;a href=&quot;https://github.com/fr3fou/flappy-go&quot;&gt;fr3fou/flappy-go&lt;/a&gt; which plays flappy bird.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/videos/flappy-ai.mp4&quot; alt=&quot;autoplay&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;How it works&lt;/h2&gt;
&lt;p&gt;The core game is reused and built on top of using composition&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type Game struct {
    *flappy.Game
    // extra fields only used from the AI
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;type Bird struct {
    *flappy.Bird
    // extra fields only used from the AI
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;AI Architecture&lt;/h2&gt;
&lt;p&gt;500 birds in total are put in the game with the following architecture of their &quot;brain&quot;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var BrainLayers = []gone.Layer{
	{
		Nodes:     5,
		Activator: gone.Sigmoid(),
	},
	{
		Nodes:     8,
		Activator: gone.Sigmoid(),
	},
	{
		Nodes:     2,
		Activator: gone.Sigmoid(), // ideally should be softmax
	},
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are 5 inputs, 4 of them in the following diagram and the last 1 is the velocity of the bird. $\alpha, \beta, \gamma, \delta$ describe the position of the bird relative to the next pipe and world.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/flappy-ai.svg&quot; alt=&quot;flappy-ai.svg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The 2 outputs determine whether the bird should jump or not&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if output[0] &amp;gt; output[1] {
    bird.Jump()
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Their score is determined by how well they perform (how long they survive)&lt;/p&gt;
&lt;p&gt;At the end of each generation, the best performing birds are to mate.&lt;/p&gt;
&lt;p&gt;Using the &lt;code&gt;crossoverOdds&lt;/code&gt; and &lt;code&gt;mutationRate&lt;/code&gt; arguments to &lt;code&gt;ai.New()&lt;/code&gt; it is determined what rate of the new population should be created using &lt;em&gt;only&lt;/em&gt; mutation or crossover &lt;em&gt;and&lt;/em&gt; mutation.&lt;/p&gt;
&lt;p&gt;After a new population has been created, the process gets repeated.&lt;/p&gt;
</content:encoded></item><item><title>Sugoku</title><link>https://simo.sh/projects/sugoku?utm_source=rss</link><guid isPermaLink="true">https://simo.sh/projects/sugoku?utm_source=rss</guid><description>Sudoku solver using backtracking with an SDL2 visualiser</description><pubDate>Fri, 06 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Sugoku is a sudoku backtracking solver written in Go. You can explore the source code &lt;a href=&quot;https://github.com/fr3fou/sugoku&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item></channel></rss>