<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Software-DE Archives - Nerd Corner</title>
	<atom:link href="https://nerd-corner.com/de/category/software-de/feed/" rel="self" type="application/rss+xml" />
	<link>https://nerd-corner.com/de/category/software-de/</link>
	<description>Craft your dreams!</description>
	<lastBuildDate>Sun, 22 Jun 2025 17:30:00 +0000</lastBuildDate>
	<language>de</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.8.2</generator>

<image>
	<url>https://nerd-corner.com/wp-content/uploads/2019/10/cropped-LogoNerdCorner-2-32x32.png</url>
	<title>Software-DE Archives - Nerd Corner</title>
	<link>https://nerd-corner.com/de/category/software-de/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Deployment einer WebApp mit Kubernetes und Caddy</title>
		<link>https://nerd-corner.com/de/deployment-einer-webapp-mit-kubernetes-und-caddy/</link>
					<comments>https://nerd-corner.com/de/deployment-einer-webapp-mit-kubernetes-und-caddy/#respond</comments>
		
		<dc:creator><![CDATA[Nerds]]></dc:creator>
		<pubDate>Sun, 16 Feb 2025 17:20:29 +0000</pubDate>
				<category><![CDATA[Angular-DE]]></category>
		<category><![CDATA[App Entwicklung]]></category>
		<category><![CDATA[Software-DE]]></category>
		<category><![CDATA[angular]]></category>
		<category><![CDATA[Caddy]]></category>
		<category><![CDATA[Caddy Server]]></category>
		<category><![CDATA[Cluster]]></category>
		<category><![CDATA[ClusterIP]]></category>
		<category><![CDATA[ConfigMap]]></category>
		<category><![CDATA[Container]]></category>
		<category><![CDATA[Deployment]]></category>
		<category><![CDATA[Docker]]></category>
		<category><![CDATA[Domain]]></category>
		<category><![CDATA[Domain Registrierung]]></category>
		<category><![CDATA[Hetzner]]></category>
		<category><![CDATA[Images]]></category>
		<category><![CDATA[Ingress]]></category>
		<category><![CDATA[Ingress Controller]]></category>
		<category><![CDATA[IP Adresse]]></category>
		<category><![CDATA[K3s]]></category>
		<category><![CDATA[Kubectl]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[LoadBalancer]]></category>
		<category><![CDATA[mySql]]></category>
		<category><![CDATA[Nest.js]]></category>
		<category><![CDATA[Node Port]]></category>
		<category><![CDATA[Pods]]></category>
		<category><![CDATA[Redis]]></category>
		<category><![CDATA[Reverse Proxy]]></category>
		<category><![CDATA[Schritt für Schritt Anweisung]]></category>
		<category><![CDATA[Server]]></category>
		<category><![CDATA[Service]]></category>
		<category><![CDATA[Traefik]]></category>
		<guid isPermaLink="false">https://nerd-corner.com/?p=1687</guid>

					<description><![CDATA[<p>Nachdem im letzten Beitrag beschrieben wurde, wie man produktionsreife Docker-Images erstellt und auf Docker Hub hochlädt, geht es nun darum, diese Images auf einem Server &#8230; </p>
<p>The post <a href="https://nerd-corner.com/de/deployment-einer-webapp-mit-kubernetes-und-caddy/">Deployment einer WebApp mit Kubernetes und Caddy</a> appeared first on <a href="https://nerd-corner.com/de">Nerd Corner</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Nachdem im letzten Beitrag beschrieben wurde, wie man produktionsreife Docker-Images erstellt und auf Docker Hub hochlädt, geht es nun darum, diese Images auf einem Server bereitzustellen. Ziel ist es, die Webanwendung über eine eigene Domain für alle zugänglich zu machen. Dafür nutzen wir einen Virtual Private Server (VPS) von Hetzner und setzen Kubernetes (<span style="font-size: 1.125rem;">k3s) </span><span style="font-size: 1.125rem;">mit Caddy als Reverse Proxy ein.</span></p>
<p><em><strong>Das könnte dich ebenfalls interessieren: </strong><a href="https://nerd-corner.com/de/docker-images-erstellen-und-auf-docker-hub-hochladen/">Docker Images erstellen und auf Dockerhub hochladen</a></em></p>
<h2>Einen VPS bei Hetzner einrichten</h2>
<p>Hetzner bietet oft Empfehlungslinks mit Guthaben-Vorteilen für Neukunden. Selbstverständlich kann man auch andere Anbieter nutzen, aber Hetzner ist preislich attraktiv und bietet solide Leistungen.</p>
<h3>Was ist ein VPS?</h3>
<p>Ein Virtual Private Server (VPS) ist ein virtueller Server, der auf einer physischen Maschine betrieben wird und als eigenständiger Server agiert. Er bietet mehr Kontrolle als klassisches Shared Hosting und ist eine kostengünstige Alternative zu dedizierten Servern. Der Zugriff erfolgt in der Regel per SSH (Secure Shell), wodurch wir den Server über die Kommandozeile steuern können.</p>
<h3>SSH-Zugriff auf den VPS</h3>
<p data-start="1137" data-end="1384">Nach dem Erstellen eines VPS erfolgt die Verwaltung meist über eine <strong data-start="1205" data-end="1227">Secure Shell (SSH)</strong>. SSH ist ein Protokoll, das verschlüsselte Verbindungen zu entfernten Servern ermöglicht. Um sich mit dem Server zu verbinden, nutzt man folgenden Befehl:</p>
<p data-start="1137" data-end="1384"><span style="background-color: #e9ebec; color: #222222; font-family: Monaco, Consolas, 'Andale Mono', 'DejaVu Sans Mono', monospace; font-size: 15px;">ssh root@&lt;IP-Adresse-des-Servers&gt;</span></p>
<p data-start="1430" data-end="1582">Falls ein SSH-Schlüssel hinterlegt wurde, kann die Authentifizierung per <strong data-start="1503" data-end="1535">Public-Key-Authentifizierung</strong> erfolgen, was sicherer als ein Passwort ist.</p>
<h3>Server bei Hetzner erstellen</h3>
<ol>
<li>Nach der Anmeldung in der Hetzner Cloud navigieren wir zu „Projekte“ und erstellen ein neues Projekt.</li>
<li>Danach wählen wir „Server hinzufügen“ und können eine Instanz konfigurieren.</li>
<li>Für den Anfang reicht oft das günstigste Modell aus. Ich empfehle jedoch, die Option für eine IPv4-Adresse zu aktivieren, da rein IPv6-basierte Setups oft Kompatibilitätsprobleme verursachen.</li>
</ol>
<h2>Domain einrichten</h2>
<p>Um die Anwendung später unter einer eigenen Domain zu erreichen, muss man eine Domain registrieren und mit dem Server verknüpfen.</p>
<h3>Domain bei Hetzner beantragen</h3>
<ol>
<li>In der Hetzner ConsoleH eine neue Domain registrieren oder eine bestehende Domain hinzufügen.</li>
<li>Um DNS-Einträge zu verwalten, müssen wir den DNS-Zugriff aktivieren.</li>
</ol>
<h3>Nameserver setzen</h3>
<p>Folgende Nameserver sollten genutzt werden:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">helium.ns.hetzner.de. 
hydrogen.ns.hetzner.com. 
oxygen.ns.hetzner.com.</pre>
<p>Diese neuen Nameserver bieten bessere Performance und Flexibilität im Vergleich zu den alten Hetzner-Nameservern:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">ns1.first-ns.de.
robotns2.second-ns.de.
robotns3.second-ns.com.</pre>
<p>Beide Nameserver Varianten sind aber möglich! Die DNS Änderungen brauchen etwas Zeit. Mit Tools wie <a href="https://mxtoolbox.com/">MXToolbox</a> können wir aber überprüfen, ob die Änderungen bereits stattgefunden haben.</p>
<h3>Domain mit dem Server verbinden</h3>
<p data-start="3155" data-end="3229">Nun muss die <strong data-start="3168" data-end="3219">IP-Adresse des Servers mit der Domain verknüpft</strong> werden:</p>
<ol data-start="3231" data-end="3512">
<li data-start="3231" data-end="3283">In der Hetzner Cloud zu <strong data-start="3258" data-end="3271">DNS-Zonen</strong> wechseln.</li>
<li data-start="3284" data-end="3323">Die registrierte Domain auswählen.</li>
<li data-start="3324" data-end="3411">Einen neuen <strong data-start="3339" data-end="3351">A-Record</strong> erstellen und die <strong data-start="3370" data-end="3386">IPv4-Adresse</strong> des Servers eintragen.</li>
<li data-start="3412" data-end="3512">Falls vorhanden, den <strong data-start="3436" data-end="3458">IPv6-Record (AAAA)</strong> entfernen, um Kompatibilitätsprobleme zu vermeiden.</li>
</ol>
<p data-start="3514" data-end="3621">Mit <a href="https://mxtoolbox.com/" target="_blank" rel="noopener" data-start="3518" data-end="3553">MXToolbox</a> kann man auch hier prüfen, ob die DNS-Änderungen bereits übernommen wurden.</p>
<h2>Kubernetes einrichten</h2>
<p>Kubernetes ist ein leistungsfähiges Orchestrierungstool für Container. Ich verwende <strong data-start="3756" data-end="3763">k3s</strong>, eine schlanke Kubernetes-Variante, die sich besonders gut für kleinere Umgebungen eignet.</p>
<h3 data-start="3858" data-end="3902">K3s auf dem Server installieren</h3>
<p data-start="3903" data-end="3980">Per SSH auf den Server verbinden und k3s mit folgendem Befehl installieren:</p>
<pre><code class="language-sh">curl -sfL https://get.k3s.io | sh - </code></pre>
<p><span style="font-size: 1.125rem;">Das Skript installiert k3s und startet den Kubernetes-Dienst. </span>Nach der Installation kann k3s mit folgendem Befehl überprüft werden:</p>
<pre><code class="language-sh">kubectl get nodes
</code></pre>
<p>k3s bringt eine eigene <code>kubectl</code>-Version mit, sodass keine separate Installation nötig ist.</p>
<h3>YAML-Files für FE, BE, MySQL und Redis erstellen</h3>
<p>Für das Deployment unserer Anwendung brauchen wir YAML-Dateien für:</p>
<ul>
<li>Frontend (Angular)</li>
<li>Backend (NestJS)</li>
<li>Datenbank (MySQL)</li>
<li>Session-Management (Redis)</li>
</ul>
<p>Eine <strong data-start="4641" data-end="4661">Deployment-Datei</strong> für das Backend könnte so aussehen:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: backend
  template:
    metadata:
      labels:
        app: backend
    spec:
      containers:
        - name: backend
          image: dockerhub-user/backend:latest
          ports:
            - containerPort: 3000
---
apiVersion: v1
kind: Service
metadata:
  name: backend-service
spec:
  selector:
    app: backend
  ports:
    - protocol: TCP
      port: 80
      targetPort: 3000
  type: ClusterIP
</pre>
<h2>Was sind Deployments und Services?</h2>
<ul>
<li><strong>Deployments</strong> verwalten die Bereitstellung und Skalierung von Containern.</li>
<li><strong>Services</strong> sorgen für eine stabile Netzwerkverbindung zwischen Containern.</li>
<li>ClusterIP bedeutet, dass der Service nur innerhalb des Kubernetes-Clusters erreichbar ist.</li>
</ul>
<h2>Caddy als Reverse Proxy einrichten</h2>
<p>Damit eingehender Traffic richtig verteilt wird, wird ein Reverse Proxy benötigt. K3s bringt standardmäßig <strong>Traefik</strong> mit, allerdings habe ich mich für eine einfachere Lösung entschieden: <strong>Caddy</strong>. Ich war echt erstaunt wie wenig Anleitungen bzw. Dokumentation es zu Caddy in Kombination mit Kubernetes gibt.</p>
<h3>Warum Kubernetes mit Caddy?</h3>
<ul>
<li>Automatische Let’s Encrypt SSL-Zertifikate</li>
<li>Einfache Konfiguration per <code>Caddyfile</code></li>
<li>Built-in Load Balancer</li>
</ul>
<h3>Traefik entfernen</h3>
<pre><code class="language-sh">kubectl delete helmrelease traefik -n kube-system
</code></pre>
<h3>Caddy Deployment erstellen</h3>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">apiVersion: apps/v1
kind: Deployment
metadata:
  name: caddy
spec:
  replicas: 1
  selector:
    matchLabels:
      app: caddy
  template:
    metadata:
      labels:
        app: caddy
    spec:
      containers:
        - name: caddy
          image: caddy
          volumeMounts:
            - name: caddy-config
              mountPath: /etc/caddy/Caddyfile
      volumes:
        - name: caddy-config
          configMap:
            name: caddy-config
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: caddy-config
data:
  Caddyfile: |

    example.com {
        reverse_proxy backend-service:3000
    }

---
apiVersion: v1
kind: Service
metadata:
  name: caddy-service
spec:
  type: LoadBalancer
  selector:
    app: caddy
  ports:
    - port: 80
      targetPort: 80
    - port: 443
      targetPort: 443</pre>
<p>Wichtig: Da Let’s Encrypt ein Rate-Limit hat, sollten Tests zunächst mit Staging-Zertifikaten erfolgen!</p>
<h2>Fazit</h2>
<p>Nach diesen Schritten läuft die Anwendung nun in einem Kubernetes-Cluster auf einem Hetzner VPS und ist über eine eigene Domain erreichbar. Als nächstes würde es sich anbieten eine automatische CI/CD-Pipeline einzurichten, um neue Versionen ohne manuellen Aufwand zu deployen.</p>
<p>The post <a href="https://nerd-corner.com/de/deployment-einer-webapp-mit-kubernetes-und-caddy/">Deployment einer WebApp mit Kubernetes und Caddy</a> appeared first on <a href="https://nerd-corner.com/de">Nerd Corner</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://nerd-corner.com/de/deployment-einer-webapp-mit-kubernetes-und-caddy/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Docker Images erstellen und auf Docker Hub hochladen</title>
		<link>https://nerd-corner.com/de/docker-images-erstellen-und-auf-docker-hub-hochladen/</link>
					<comments>https://nerd-corner.com/de/docker-images-erstellen-und-auf-docker-hub-hochladen/#respond</comments>
		
		<dc:creator><![CDATA[Nerds]]></dc:creator>
		<pubDate>Wed, 15 Jan 2025 11:44:13 +0000</pubDate>
				<category><![CDATA[Angular-DE]]></category>
		<category><![CDATA[App Entwicklung]]></category>
		<category><![CDATA[Software-DE]]></category>
		<category><![CDATA[angular]]></category>
		<category><![CDATA[Anleitung]]></category>
		<category><![CDATA[Backend]]></category>
		<category><![CDATA[Backend Server]]></category>
		<category><![CDATA[Docker]]></category>
		<category><![CDATA[Docker Hub]]></category>
		<category><![CDATA[Docker Images]]></category>
		<category><![CDATA[Docker Repository]]></category>
		<category><![CDATA[frontend]]></category>
		<category><![CDATA[google cloud mysql]]></category>
		<category><![CDATA[Images]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[mySql]]></category>
		<category><![CDATA[Nest]]></category>
		<category><![CDATA[Nest.js]]></category>
		<category><![CDATA[Redis]]></category>
		<category><![CDATA[redis session management]]></category>
		<category><![CDATA[redis session management nest.js]]></category>
		<category><![CDATA[Repository]]></category>
		<category><![CDATA[Schritt für Schritt Anweisung]]></category>
		<guid isPermaLink="false">https://nerd-corner.com/?p=1680</guid>

					<description><![CDATA[<p>In diesem Beitrag zeige ich, wie man production-ready Docker Images für eine Web Anwendung mit Angular, NestJS, MySQL und Redis erstellt und anschließend auf Docker &#8230; </p>
<p>The post <a href="https://nerd-corner.com/de/docker-images-erstellen-und-auf-docker-hub-hochladen/">Docker Images erstellen und auf Docker Hub hochladen</a> appeared first on <a href="https://nerd-corner.com/de">Nerd Corner</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>In diesem Beitrag zeige ich, wie man production-ready Docker Images für eine Web Anwendung mit Angular, NestJS, MySQL und Redis erstellt und anschließend auf Docker Hub veröffentlicht. Voraussetzung ist eine installierte Docker Umgebung.</p>
<p><em><strong>Das könnte dich ebenfalls interessieren:</strong> <a href="https://nerd-corner.com/de/erfahrungen-aus-der-praxis-nestjs-auf-vercel-hosten/">NestJS auf Vercel hosten</a></em></p>
<h2>Erstellung der Docker Compose Yml</h2>
<p>Mit Docker Compose können alle Komponenten einer Anwendung über eine einzige Konfigurationsdatei definiert und gemeinsam gebuildet bzw. gestartet werden.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">version: '3.8'

services:
  frontend:
    build: ./frontend
    ports:
      - "80:80"
    depends_on:
      - backend

  backend:
    build: ./backend
    ports:
      - "3000:3000"
    depends_on:
      - mysql
      - redis
    environment:
      - DATABASE_URL=mysql://user:password@mysql:3306/db
      - SESSION_STORE=redis://redis:6379

  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: db
    ports:
      - "3306:3306"

  redis:
    image: redis:latest
    ports:
      - "6379:6379"</pre>
<h2>Erstellung der Env Datei</h2>
<p>Um Umgebungsvariablen zentral zu verwalten, erstellen wir eine <code>.env</code> Datei:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">DATABASE_URL=mysql://user:password@mysql:3306/db 
SESSION_STORE=redis://redis:6379</pre>
<p>Wichtig: Alle ENV-Variablen, die im Code verwendet werden, müssen auch in <code>docker-compose.yml</code> vorkommen!</p>
<h2>Docker-Image für das Frontend</h2>
<p>Das Angular-Frontend muss für die Produktion gebaut werden. Hier ein Beispiel-<code>Dockerfile</code>:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">FROM node:20 AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm install
COPY . .
RUN npm run build --prod

FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf</pre>
<p>Da wir für den Build auf nginx angewiesen sind brauchen wir auch eine entsprechende Config Datei:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">server {
  listen 80;
  server_name _;

  location / {
    root /usr/share/nginx/html;
    index index.html;
    try_files $uri $uri/ /index.html;
  }
}</pre>
<h2>Docker-Image für das Backend</h2>
<p>Auch das NestJS-Backend muss gebaut werden. Hier ein optimiertes <code>Dockerfile</code></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic"># Build stage
FROM node:20 AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm install
COPY . .
RUN npm run build

# Production stage
FROM node:20-alpine
WORKDIR /app

COPY --from=build /app/dist ./dist
COPY package.json package-lock.json ./
RUN npm install --only=production
CMD ["node", "dist/main.js"]</pre>
<h2>Builden der App mit Docker Compose</h2>
<p>Nachdem alle Dockerfiles konfiguriert wurden können jetzt die Images gebuildet werden. Mit Docker compose ist das ganze wirklich einfach.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">docker compose up -d --build</pre>
<p>Anschließend sind die Images fertig, die Container gebaut und die App kann lokal direkt ausgetestet werden! Als letzer Schritt müssen die Images noch auf DockerHub hochgeladen werden, damit sie später leichter für das Deployment auf einem Server genutzt werden können.</p>
<h2>Hochladen auf Docker Hub</h2>
<p>Das Hochladen wird nachfolgend Schritt für Schritt erklärt:</p>
<ol>
<li>Erstelle einen Account bei <a href="https://hub.docker.com/">Docker Hub</a>.</li>
<li>Erstelle ein Repository für das Frontend und Backend (1 privates Repo ist aktuell kostenlos).</li>
<li>Baue die Images und tagge sie:
<pre class="EnlighterJSRAW" data-enlighter-language="generic">docker tag &lt;image-id&gt; dockerAccountName/frontend:latest
docker tag &lt;image-id&gt; dockerAccountName/backend:latest</pre>
</li>
<li>Melde dich an und pushe die Images:
<pre class="EnlighterJSRAW" data-enlighter-language="generic">docker login
docker push dockerAccountName/frontend:latest
docker push dockerAccountName/backend:latest</pre>
</li>
</ol>
<h2>Ausblick: Deployment mit Kubernetes</h2>
<p>Da die Images nun auf Docker Hub sind, steht dem Deployment nichts mehr im Wege. Ich habe mich für ein Kubernetes-Cluster auf einem Hetzner-VPS entschieden. <a href="https://nerd-corner.com/de/deployment-einer-webapp-mit-kubernetes-und-caddy/">Mehr Infos dazu hier</a>.</p>
<p>The post <a href="https://nerd-corner.com/de/docker-images-erstellen-und-auf-docker-hub-hochladen/">Docker Images erstellen und auf Docker Hub hochladen</a> appeared first on <a href="https://nerd-corner.com/de">Nerd Corner</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://nerd-corner.com/de/docker-images-erstellen-und-auf-docker-hub-hochladen/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>DIY Tastenfeldschloss &#8211; 3D Druck und Programmierung</title>
		<link>https://nerd-corner.com/de/diy-tastenfeldschloss-3d-druck-und-programmierung/</link>
					<comments>https://nerd-corner.com/de/diy-tastenfeldschloss-3d-druck-und-programmierung/#respond</comments>
		
		<dc:creator><![CDATA[Nerds]]></dc:creator>
		<pubDate>Tue, 17 Dec 2024 15:10:58 +0000</pubDate>
				<category><![CDATA[Arduino Projekte]]></category>
		<category><![CDATA[DIY]]></category>
		<category><![CDATA[Hardware-DE]]></category>
		<category><![CDATA[Software-DE]]></category>
		<category><![CDATA[SolidWorks-DE]]></category>
		<category><![CDATA[3D Druck]]></category>
		<category><![CDATA[Arduino]]></category>
		<category><![CDATA[C Programmierung]]></category>
		<category><![CDATA[CAD]]></category>
		<category><![CDATA[Fritzing]]></category>
		<category><![CDATA[Gehäuse]]></category>
		<category><![CDATA[Genauigkeit]]></category>
		<category><![CDATA[Hardware]]></category>
		<category><![CDATA[Präzision]]></category>
		<category><![CDATA[Schloss]]></category>
		<category><![CDATA[Solid Works]]></category>
		<category><![CDATA[Tastenfeld]]></category>
		<category><![CDATA[Tastenfeldschloss]]></category>
		<category><![CDATA[Verkabelung]]></category>
		<category><![CDATA[Version 1]]></category>
		<guid isPermaLink="false">https://nerd-corner.com/?p=1642</guid>

					<description><![CDATA[<p>Ein Schrank, der immer abgeschlossen sein soll, und fünf Personen, die darauf zugreifen müssen – eine klassische Herausforderung. Die naheliegenden Lösungen? Fünf Schlüssel im Umlauf &#8230; </p>
<p>The post <a href="https://nerd-corner.com/de/diy-tastenfeldschloss-3d-druck-und-programmierung/">DIY Tastenfeldschloss &#8211; 3D Druck und Programmierung</a> appeared first on <a href="https://nerd-corner.com/de">Nerd Corner</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p data-start="111" data-end="523">Ein Schrank, der immer abgeschlossen sein soll, und fünf Personen, die darauf zugreifen müssen – eine klassische Herausforderung. Die naheliegenden Lösungen? Fünf Schlüssel im Umlauf oder eine einzige Person, die den Schlüssel verwaltet, sodass man ihn sich jedes Mal ausleihen muss. Doch wir kennen das alle: Am Ende liegt der Schlüssel dann doch auf dem Schrank, unter dem Teppich oder hinter dem Blumentopf.</p>
<p data-start="525" data-end="926">Aber wäre es nicht viel praktischer, ganz auf physische Schlüssel zu verzichten? Heutzutage gibt es zahlreiche Möglichkeiten, ein Schloss zu öffnen: Fingerabdruckscanner, Gesichtserkennung, NFC, Zahlencodes, Stellräder – oder natürlich brachiale Methoden wie Sprengstoff und rohe Gewalt. Doch wenn man eine preiswerte, gewaltfreie und einfache Lösung sucht, rückt das Tastenfeldschloss in den Fokus.</p>
<p data-start="928" data-end="1117">Überraschenderweise gibt es im Internet kaum wirklich gute DIY-Lösungen für Hobbybastler. Also packe ich es selbst an – mein erstes Tastenfeld-Schloss, das ich schlicht „Version 1“ nenne.</p>
<p data-start="928" data-end="1117"><em><strong>Das könnte dich auch interessieren: </strong><a href="https://nerd-corner.com/de/diy-powerbank-mit-spannungsregler-und-voltmeter/">DIY Powerbank mit Spannungsregler und Voltmeter</a></em></p>
<h2>Konstruktion des Gehäuses</h2>
<p data-start="89" data-end="285">Zunächst liegt der Fokus auf dem Gehäuse und der Aufnahme für das Tastenfeld. Die erste Frage, die sich dabei immer stellt: Wie groß soll es sein? Die Antwort hängt von mehreren Faktoren ab:</p>
<ul data-start="287" data-end="664">
<li data-start="287" data-end="390">Welche Komponenten werden benötigt? Jedes Bauteil nimmt Platz ein und beeinflusst die Bauweise.</li>
<li data-start="391" data-end="529">Wie viel Raum beanspruchen die Komponenten? Ein kompaktes Design ist vorteilhaft, darf aber nicht die Funktionalität einschränken.</li>
<li data-start="530" data-end="664">Wie sind Haptik und Bedienbarkeit? Das Tastenfeld sollte angenehm zu bedienen sein, ohne dass es zu eng oder unpraktisch wird.</li>
</ul>
<p data-start="666" data-end="784">Diese Überlegungen bilden die Grundlage für das Gehäusedesign – denn eine gute Planung spart später Zeit und Nerven.</p>
<h3 data-start="98" data-end="130">Was kommt ins Gehäuse?</h3>
<p data-start="132" data-end="238">Zentraler Bestandteil ist natürlich das Membrantastenfeld (1.0.1). Es hat die folgenden Abmessungen:</p>
<ul data-start="240" data-end="335">
<li data-start="240" data-end="261">Breite: 69 mm</li>
<li data-start="262" data-end="282">Länge: 76 mm</li>
<li data-start="283" data-end="335">Dicke: 0,6 mm (bzw. 0,95 mm über den Tasten)</li>
</ul>
<p data-start="337" data-end="515">Zusätzlich verfügt das Tastenfeld über ein Flachbandkabel mit DuPont-Buchsen zum Anschluss an einen Mikrocontroller. Das Kabel selbst ist 85 mm lang und 17,78 mm breit.</p>
<p data-start="517" data-end="773">Die Schaltzentrale des Schlosses bildet der Nano (1.0.2). Um ihn sauber im Gehäuse unterzubringen und die Kabelverbindungen möglichst komfortabel zu gestalten, habe ich mich für ein Nano-Expansionsboard mit Schraubklemmen (1.0.4) entschieden.</p>
<p data-start="775" data-end="938">Für die Notstromversorgung kommt eine Hohlbuchse (5,5 x 2,1 mm, 1.0.4) zum Einsatz, damit das Schloss auch bei einem Stromausfall weiterhin funktioniert.</p>
<p><img fetchpriority="high" decoding="async" class="aligncenter wp-image-1671 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/1-0-1.jpg" alt="tastenfeldschloss Bauteile" width="2310" height="1324" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/1-0-1.jpg 2318w, https://nerd-corner.com/wp-content/uploads/2024/12/1-0-1-300x172.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/1-0-1-1024x587.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/1-0-1-768x440.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/1-0-1-1536x881.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/1-0-1-2048x1174.jpg 2048w" sizes="(max-width: 2310px) 100vw, 2310px" /></p>
<p>Die Stift- und Buchsenleisten (1.1.1) dienen als zentrale Stromverteilung und werden später auf die Lochrasterplatte (1.1.2) gelötet. Damit alle Komponenten zuverlässig verbunden werden, kommen Jumperkabel (1.1.3) zum Einsatz. Je nach Position der Bauteile sind unterschiedliche Längen erforderlich – in diesem Fall 10 cm und 20 cm.</p>
<p><img decoding="async" class="aligncenter wp-image-1670 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/1-1.jpg" alt="" width="2310" height="1327" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/1-1.jpg 2321w, https://nerd-corner.com/wp-content/uploads/2024/12/1-1-300x172.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/1-1-1024x588.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/1-1-768x441.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/1-1-1536x882.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/1-1-2048x1176.jpg 2048w" sizes="(max-width: 2310px) 100vw, 2310px" /></p>
<p data-start="71" data-end="316">Für die Statusanzeige des Tastenfeldschlosses setze ich auf Neo-Pixel – adressierbare LEDs vom Typ WS2812b (1.2.1). Damit lassen sich verschiedene Farben und Effekte steuern, um den aktuellen Zustand des Schlosses visuell darzustellen.</p>
<p data-start="318" data-end="394">Auf die Positionierung der Schrauben gehe ich später noch genauer ein.</p>
<p><img decoding="async" class="aligncenter wp-image-1669 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/1-2.jpg" alt="" width="2320" height="1324" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/1-2.jpg 2331w, https://nerd-corner.com/wp-content/uploads/2024/12/1-2-300x171.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/1-2-1024x584.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/1-2-768x438.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/1-2-1536x876.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/1-2-2048x1169.jpg 2048w, https://nerd-corner.com/wp-content/uploads/2024/12/1-2-1400x800.jpg 1400w" sizes="(max-width: 2320px) 100vw, 2320px" /></p>
<p data-start="99" data-end="320">Nachdem die Komponenten nun feststehen, kann ich mir Gedanken über die Größe des Gehäuses machen. Diese wird nicht nur durch die verbauten Elemente bestimmt, sondern vor allem durch die Bedienbarkeit und Haptik.</p>
<p data-start="322" data-end="804">Tastenfelder begegnen uns täglich – an Bankautomaten, Telefonen, Türschlossanlagen und natürlich Smartphones. Die Entscheidung für die Tiefe des Gehäuses basiert auf einer positiven Erinnerung an meinen vorletzten Arbeitsplatz: Das dortige Tastenfeldschloss am Eingang war erhöht und von beiden Seiten gut zugänglich. Man konnte es bequem mit dem rechten oder linken Daumen bedienen, und die abgerundeten Ecken sorgten für eine angenehme Haptik beim Auflegen der Hand.</p>
<h2 data-start="806" data-end="829">Tastenfeldschloss Gehäusedesign</h2>
<p data-start="830" data-end="1165">Daraus ergibt sich eine Tiefe von 45 mm (2.0.3). Für eine bessere Ergonomie erhalten die Ecken einen Radius von 15 mm (R15) und die umlaufenden oberen Kanten einen Radius von 10 mm (R10). Mir ist bewusst, dass diese Verrundungen den Innenraum leicht verkleinern, aber der Komfort und die Optik überwiegen diesen Nachteil.</p>
<p data-start="1167" data-end="1511">Die Breite und Höhe des Gehäuses ergeben sich aus den einzubauenden Komponenten. Dabei muss auch der Platz für die Verkabelung berücksichtigt werden. Besonders wichtig: Der Nano sollte im eingebauten Zustand weiterhin mit einem handelsüblichen USB-Mini-Kabel erreichbar sein, um beispielsweise neue Programme aufspielen zu können.</p>
<h3 data-start="1513" data-end="1546">Montage und Befestigung</h3>
<ul data-start="1547" data-end="2275">
<li data-start="1547" data-end="1646">Vier M3-Gewinde (2.0.1) an der Innenseite ermöglichen das Anschrauben der Trägerplatte.</li>
<li data-start="1647" data-end="1787">Zusätzlich gibt es vier Montagepunkte mit Ø4,2 mm Bohrungen zur Befestigung des Gehäuses an einer Tür, einem Deckel oder einer Wand.</li>
<li data-start="1788" data-end="1941">Das Gehäuse erhält ein Fenster (2.0.2) mit 60 × 67 mm, das für das Tastenfeld vorgesehen ist. Dieses wird später mit der Trägerplatte ausgefüllt.</li>
<li data-start="1942" data-end="2080">Haltesäulen mit M2-Gewinde sowie die Öffnung für die Hohlbuchse (1.0.4) sind mit orangen Ellipsen markiert (2.0.1, 2.0.3).</li>
<li data-start="2081" data-end="2165">Das nächste Bild (2.0.4) zeigt die Außenmaße: 110 mm Breite und 117 mm Höhe.</li>
<li data-start="2166" data-end="2275">Außerdem ist ein Durchbruch bzw. Schlitz von 50 mm Länge für die LED-Blende (2.0.5) erforderlich.</li>
</ul>
<p data-start="2277" data-end="2405">Für die Wandstärke des Gehäuses habe ich durchgehend 2 mm vorgesehen – stabil genug für den vorgesehenen Einsatzzweck.</p>
<p data-start="2407" data-end="2508">Mit der abgeschlossenen Gehäusekonstruktion können wir nun mit den weiteren Komponenten fortfahren.</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1668 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/2-0-1.jpg" alt="Tastenfeldschloss Gehäuse" width="2310" height="1330" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/2-0-1.jpg 2316w, https://nerd-corner.com/wp-content/uploads/2024/12/2-0-1-300x173.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/2-0-1-1024x589.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/2-0-1-768x442.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/2-0-1-1536x884.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/2-0-1-2048x1179.jpg 2048w" sizes="auto, (max-width: 2310px) 100vw, 2310px" /></p>
<h3 data-start="91" data-end="147">Die Trägerplatte – das zentrale Montageelement</h3>
<p data-start="149" data-end="389">Die nächste wichtige Komponente ist die Trägerplatte. Der Name erklärt sich von selbst: Bis auf die Hohlbuchse werden hier alle Bauteile befestigt. Dieses System bietet mehrere Vorteile gegenüber einer direkten Montage im Gehäuse:</p>
<p data-start="391" data-end="771"><img src="https://s.w.org/images/core/emoji/16.0.1/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Einfache Montage außerhalb des Gehäuses – Mehr Platz und bessere Handhabung bei der Verkabelung.<br data-start="493" data-end="496" /><img src="https://s.w.org/images/core/emoji/16.0.1/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Modularität – Verschiedene Trägerplatten mit unterschiedlichen Komponenten sind möglich.<br data-start="590" data-end="593" /><img src="https://s.w.org/images/core/emoji/16.0.1/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Schlichtes Gehäusedesign – Die Gehäusekonstruktion bleibt einfacher und flexibler.<br data-start="681" data-end="684" /><img src="https://s.w.org/images/core/emoji/16.0.1/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Leichter Austausch – Komponenten lassen sich einfacher ersetzen oder erweitern.</p>
<p data-start="773" data-end="870">Doch speziell für das Tastenfeldschloss gibt es noch einen weiteren entscheidenden Vorteil:</p>
<p data-start="872" data-end="1185">Das Tastenfeld wird direkt in eine vorgesehene Vertiefung der Trägerplatte (2.1.1) geklebt. Anschließend wird die Trägerplatte samt Tastenfeld von hinten in das Gehäuse eingesetzt und mit M3-Schrauben fixiert. Dabei verdeckt der Rahmen des Fensters im Gehäuse den Rand und die Kabel vollständig.</p>
<p data-start="1187" data-end="1485"><img src="https://s.w.org/images/core/emoji/16.0.1/72x72/1f512.png" alt="🔒" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Sicherheitsaspekt: Das Fenster ist so dimensioniert, dass zwar der Rand und die Kabel verborgen bleiben, aber die Tasten vollständig sichtbar und bedienbar sind. Dadurch lässt sich das Tastenfeld nicht zerstörungsfrei entfernen – ein wichtiger Schutzmechanismus gegen Manipulation.</p>
<h3 data-start="1487" data-end="1545">Befestigung der Komponenten auf der Trägerplatte</h3>
<p data-start="1547" data-end="1663">Auf der Rückseite (2.1.2) der Trägerplatte befinden sich diverse Befestigungsmöglichkeiten für die Elektronik:</p>
<p data-start="1665" data-end="2043"><img src="https://s.w.org/images/core/emoji/16.0.1/72x72/1f7e1.png" alt="🟡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> M3-Gewinde für den Nano-Adapter (2.1.3) – Da Nano-Adapter im Internet oft unterschiedliche Lochabstände haben, gibt es auf der rechten Seite (2.1.2) ein zusätzliches Befestigungsgewinde zur flexiblen Anpassung. Falls die Bohrungen dennoch nicht exakt passen, lassen sie sich vorsichtig aufweiten – allerdings ohne dabei Leiterbahnen des Adapters zu beschädigen.</p>
<p data-start="2045" data-end="2177"><img src="https://s.w.org/images/core/emoji/16.0.1/72x72/1f534.png" alt="🔴" class="wp-smiley" style="height: 1em; max-height: 1em;" /> M2-Gewinde für den Nano R3 ATMEGA168P (2.1.4) – Eine alternative, kostengünstige Lösung anstelle eines Nano R3 mit Adapter.</p>
<p data-start="2179" data-end="2366"><img src="https://s.w.org/images/core/emoji/16.0.1/72x72/1f535.png" alt="🔵" class="wp-smiley" style="height: 1em; max-height: 1em;" /> M2-Gewinde für die Lochrasterplatte (20×80 mm, 1.1.2, 2.1.5) – Diese dient zur Energieverteilung und verbindet alle Stromversorgungsleitungen sauber an einem zentralen Punkt.</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1667 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/2-1-1.jpg" alt="" width="2310" height="1329" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/2-1-1.jpg 2317w, https://nerd-corner.com/wp-content/uploads/2024/12/2-1-1-300x173.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/2-1-1-1024x589.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/2-1-1-768x442.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/2-1-1-1536x884.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/2-1-1-2048x1178.jpg 2048w" sizes="auto, (max-width: 2310px) 100vw, 2310px" /></p>
<h3 data-start="95" data-end="140">Die LED-Blende und ihre Befestigung</h3>
<p data-start="142" data-end="442">Die LED-Blende (2.2.1) wurde so konstruiert, dass sie von der Rückseite in den Schlitz (2.0.5) des Gehäuses eingeklickt wird. Der Radius an der Außenseite der LED-Blende entspricht dem Gehäuseradius, wodurch ein weicher Übergang entsteht und sich die Blende optisch nahtlos einfügt.</p>
<p data-start="444" data-end="602">Die Montagebrücke (Vorderseite 2.2.2, Rückseite 2.2.3) muss ich nicht neu entwerfen, da ich sie bereits in anderen Projekten erfolgreich verwendet habe.</p>
<h3 data-start="604" data-end="638">Die Befestigung der LEDs</h3>
<p data-start="640" data-end="814">Nun bleibt noch die Montage der drei WS2812b-LEDs (1.2.1). Warum genau drei LEDs benötigt werden, werde ich später im Programmierteil dieses Artikels erläutern.</p>
<p data-start="816" data-end="1039">Die Entwicklung des LED-Halters (SMD50, 2.2.4) war aufwendiger als erwartet. Natürlich könnte man die LED-Streifen einfach kleben, klemmen oder mit Heißkleber befestigen – doch das erschien mir zu unprofessionell.</p>
<p data-start="1041" data-end="1179">Daher habe ich viel Zeit und Mühe investiert, um eine perfekte Halterung zu entwerfen. Das Resultat ist auf Bild 2.2.5 zu sehen.</p>
<p data-start="1181" data-end="1300"><img src="https://s.w.org/images/core/emoji/16.0.1/72x72/1f4cc.png" alt="📌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Weiterführende Informationen:<br data-start="1217" data-end="1220" /><a href="https://nerd-corner.com/de/klick-und-klemmsystem-smd5050-klammer-2/">Für Details zur Konstruktion gibt es einen eigenen Artikel auf NerdCorner.</a></p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1666 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/2-2-1.jpg" alt="" width="2310" height="1330" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/2-2-1.jpg 2316w, https://nerd-corner.com/wp-content/uploads/2024/12/2-2-1-300x173.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/2-2-1-1024x589.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/2-2-1-768x442.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/2-2-1-1536x884.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/2-2-1-2048x1179.jpg 2048w" sizes="auto, (max-width: 2310px) 100vw, 2310px" /></p>
<h2 data-start="91" data-end="122">3D-Druck der Bauteile</h2>
<p data-start="124" data-end="211">Nachdem die Konstruktion abgeschlossen ist, müssen die Teile nun gedruckt werden.</p>
<p data-start="213" data-end="265"><img src="https://s.w.org/images/core/emoji/16.0.1/72x72/1f4cc.png" alt="📌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Materialwahl für die einzelnen Komponenten:</p>
<ul data-start="267" data-end="719">
<li data-start="267" data-end="359">Gehäuse (3.0.1): gedruckt mit ABS-Filament, bestehend aus Vorder- und Rückseite.</li>
<li data-start="360" data-end="421">Trägerplatte (3.0.2): gefertigt aus PLA-Filament.</li>
<li data-start="422" data-end="525">LED-Blende (3.0.3): stehend im Drucker hergestellt, gedruckt mit PLA+ in der Farbe „Natur“.</li>
<li data-start="526" data-end="628">LED-Klemmen (3.0.4): ebenfalls aus PLA+, gefertigt im selben Verfahren wie die LED-Blende.</li>
<li data-start="629" data-end="719">Brücke für die Hohlbuchse: gedruckt aus PLA-Filament, analog zur Trägerplatte.</li>
</ul>
<p data-start="721" data-end="868">Mit diesen Materialien sind die mechanischen und thermischen Eigenschaften der Bauteile optimal auf ihre jeweiligen Einsatzzwecke abgestimmt.</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1665 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/3-0-1.jpg" alt="" width="2310" height="1329" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/3-0-1.jpg 2317w, https://nerd-corner.com/wp-content/uploads/2024/12/3-0-1-300x173.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/3-0-1-1024x589.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/3-0-1-768x442.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/3-0-1-1536x884.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/3-0-1-2048x1178.jpg 2048w" sizes="auto, (max-width: 2310px) 100vw, 2310px" /></p>
<h2 data-start="80" data-end="118">Nachbearbeitung der Bauteile</h2>
<p data-start="120" data-end="224">Nach dem Druck müssen sowohl die gedruckten Teile als auch einige Kaufteile bearbeitet werden.</p>
<h4 data-start="226" data-end="266">1&#x20e3; Säubern der 3D-Druckteile</h4>
<ul data-start="267" data-end="342">
<li data-start="267" data-end="342">Entfernen von Stützmaterial und überstehenden Druckrückständen.</li>
</ul>
<h4 data-start="344" data-end="376">2&#x20e3; Gewinde schneiden</h4>
<ul data-start="377" data-end="820">
<li data-start="377" data-end="563">Gehäuse (4.0.1):
<ul data-start="404" data-end="563">
<li data-start="404" data-end="528">Vier M3-Gewinde (Achtung: Sacklöcher! Beim Schneiden vorsichtig vorgehen, um den Boden nicht nach außen zu drücken).</li>
<li data-start="531" data-end="563">Zwei M2-Gewinde (4.0.2).</li>
</ul>
</li>
<li data-start="564" data-end="820">Trägerplatte (4.0.3):
<ul data-start="596" data-end="820">
<li data-start="596" data-end="676">Die blau markierten M2-Gewinde müssen auf jeden Fall geschnitten werden.</li>
<li data-start="679" data-end="820">Bei Verwendung eines Nano-Adapters mit Schraubklemmen (2.1.4) müssen zusätzlich die rot markierten M2-Gewinde geschnitten werden.</li>
</ul>
</li>
</ul>
<h4 data-start="822" data-end="860">3&#x20e3; Lochrasterplatte kürzen</h4>
<ul data-start="861" data-end="1029">
<li data-start="861" data-end="954">Die Lochrasterplatte (4.0.4) muss auf 8 bis maximal 10 Lochraster gekürzt werden.</li>
<li data-start="955" data-end="1029">Wichtig: Die Montagelöcher sollten erhalten bleiben (siehe 4.0.5).</li>
</ul>
<h4 data-start="1031" data-end="1064">4&#x20e3; Stiftleiste kürzen</h4>
<ul data-start="1065" data-end="1152">
<li data-start="1065" data-end="1152">Die Stiftleiste mit einem Seitenschneider auf acht Pins kürzen (4.0.6).</li>
</ul>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1664 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/4-0-2.jpg" alt="" width="2310" height="1394" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/4-0-2.jpg 2316w, https://nerd-corner.com/wp-content/uploads/2024/12/4-0-2-300x181.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/4-0-2-1024x618.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/4-0-2-768x464.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/4-0-2-1536x927.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/4-0-2-2048x1236.jpg 2048w" sizes="auto, (max-width: 2310px) 100vw, 2310px" /></p>
<h3 data-start="89" data-end="115">Löten der Bauteile</h3>
<p data-start="117" data-end="221">Nun geht es ans Löten der Teile. Zuerst konzentrieren wir uns auf die Energieversorgungsplatine:</p>
<h4 data-start="223" data-end="273">1&#x20e3; Löten der Energieversorgungsplatine</h4>
<ul data-start="274" data-end="730">
<li data-start="274" data-end="375">Lochrasterplatte (4.0.5): Lötung der Sockelleiste (1.1.1) und der Pin-Leiste (4.0.6).</li>
<li data-start="376" data-end="730">Pin-Leiste (4.1.2): Eine zweireihige Pin- und Sockelleiste wird aufgelötet. Auf der Rückseite verbinden wir die beiden Reihen jeweils mit Lötzinn.
<ul data-start="541" data-end="730">
<li data-start="541" data-end="730">Die Reihen unterscheiden sich in Männchen und Weibchen sowie in den Farben: Rot für Plus und Schwarz für Minus. Dies erleichtert den Anschluss der Energieversorgung.</li>
</ul>
</li>
</ul>
<h4 data-start="732" data-end="773">2&#x20e3; Verlötung der WS2812B LEDs</h4>
<ul data-start="774" data-end="844">
<li data-start="774" data-end="844">Verlöten der Anschlüsse des WS2812B LED-Streifens (4.1.3).</li>
</ul>
<h4 data-start="846" data-end="884">3&#x20e3; Verlöten der Hohlbuchse</h4>
<ul data-start="885" data-end="1126">
<li data-start="885" data-end="952">Zum Abschluss wird die Hohlbuchse (1.0.4) verlötet (4.1.4).</li>
<li data-start="953" data-end="1126">Eine detaillierte Anleitung <a href="https://nerd-corner.com/de/hohlbuchse-als-schalter-verwenden/">zum Verlöten der Hohlbuchse</a> findest du in einem separaten Artikel. Es ist wichtig, die genaue Vorgehensweise zu kennen, um Fehler zu vermeiden.</li>
</ul>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1663 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/4-1-2.jpg" alt="" width="2310" height="1329" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/4-1-2.jpg 2317w, https://nerd-corner.com/wp-content/uploads/2024/12/4-1-2-300x173.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/4-1-2-1024x589.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/4-1-2-768x442.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/4-1-2-1536x884.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/4-1-2-2048x1178.jpg 2048w" sizes="auto, (max-width: 2310px) 100vw, 2310px" /></p>
<h2 data-start="75" data-end="118">Zusammenbau des Tastenfeldschlosses</h2>
<h4 data-start="120" data-end="152">1&#x20e3; LED-Blende einbauen</h4>
<ul data-start="153" data-end="332">
<li data-start="153" data-end="217">Zuerst klicke die LED-Blende (2.2.1) in das Gehäuse ein.</li>
<li data-start="218" data-end="332">Verklebe die LED-Blende an den vorgesehenen Stellen, wie auf den Bildern 5.0.1 und 5.0.2 abgebildet.</li>
</ul>
<h4 data-start="334" data-end="365">2&#x20e3; Nano R3 einstecken</h4>
<ul data-start="366" data-end="447">
<li data-start="366" data-end="447">Stecke den Nano R3 (1.0.2) in den Nano-Adapter (5.0.3) deiner Wahl ein.</li>
</ul>
<h4 data-start="449" data-end="484">3&#x20e3; Nano-Adapter montieren</h4>
<ul data-start="485" data-end="759">
<li data-start="485" data-end="617">Schraube den Nano-Adapter (1.0.3) mit dem eingesteckten Nano R3 (1.0.2) auf der Trägerplatte-Rückseite (4.0.3) fest.</li>
<li data-start="618" data-end="759">Verwende dafür die M3-Gewinde auf der Trägerplatte und die Schrauben (1.2.2), wie auf den Bildern 5.0.4 und 5.0.5 zu sehen.</li>
</ul>
<h4 data-start="761" data-end="801">4&#x20e3; Energieversorgung anbringen</h4>
<ul data-start="802" data-end="955">
<li data-start="802" data-end="922">Befestige die Energieversorgung (4.1.2) mit den Schrauben (1.2.4) an der Trägerplatte-Rückseite (4.0.3).</li>
<li data-start="923" data-end="955">Siehe dazu das Bild 5.0.6.</li>
</ul>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1662 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/5-0-1.jpg" alt="" width="2310" height="1330" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/5-0-1.jpg 2316w, https://nerd-corner.com/wp-content/uploads/2024/12/5-0-1-300x173.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/5-0-1-1024x589.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/5-0-1-768x442.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/5-0-1-1536x884.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/5-0-1-2048x1179.jpg 2048w" sizes="auto, (max-width: 2310px) 100vw, 2310px" /></p>
<h3 data-start="75" data-end="127">Montage des Tastenfelds auf der Trägerplatte</h3>
<h4 data-start="129" data-end="165">1&#x20e3; Wahl der Schraubenlänge</h4>
<ul data-start="166" data-end="437">
<li data-start="166" data-end="258">Achte darauf, wie auf Bild 5.1.1 dargestellt, die richtige Schraubenlänge zu wählen.</li>
<li data-start="259" data-end="437">Schrauben dürfen nicht zu lang sein, da sie sonst beim Anbringen des Tastenfelds aus der Klebefläche herausragen und das Tastenfeld nicht sauber aufgeklebt werden kann.</li>
</ul>
<h4 data-start="439" data-end="474">2&#x20e3; Tastenfeld vorbereiten</h4>
<ul data-start="475" data-end="780">
<li data-start="475" data-end="532">Entferne die Schutzfolie des Tastenfelds (1.0.1).</li>
<li data-start="533" data-end="689">Um die Folie zu entfernen, benutze ein Teppichmesser, um vorsichtig an einer Ecke zwischen Klebeschicht und Schutzfolie zu kommen (siehe 5.1.2).</li>
<li data-start="690" data-end="780">Nachdem du die Ecke erreicht hast, ziehe die ganze Schutzfolie ab (siehe 5.1.3).</li>
</ul>
<h4 data-start="782" data-end="815">3&#x20e3; Tastenfeld aufkleben</h4>
<ul data-start="816" data-end="1144">
<li data-start="816" data-end="942">Setze das Tastenfeld (1.0.1) in die Vertiefung auf der Trägerplatte-Vorderseite (2.1.1) ein und drücke es fest an.</li>
<li data-start="943" data-end="1060">Das Tastenfeld muss komplett versenkt werden und darf nicht über den Rand hinausstehen (siehe 5.1.4).</li>
<li data-start="1061" data-end="1144">Achte darauf, dass auch die Kabel in der Vertiefung liegen (siehe 5.1.5).</li>
</ul>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1661 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/5-1-1.jpg" alt="" width="2340" height="1327" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/5-1-1.jpg 2350w, https://nerd-corner.com/wp-content/uploads/2024/12/5-1-1-300x170.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/5-1-1-1024x581.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/5-1-1-768x436.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/5-1-1-1536x871.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/5-1-1-2048x1162.jpg 2048w" sizes="auto, (max-width: 2340px) 100vw, 2340px" /></p>
<h3 data-start="64" data-end="123">Verkabelung des Tastenfelds mit dem Arduino Nano R3</h3>
<h4 data-start="125" data-end="160">1&#x20e3; Jumper-Kabel verwenden</h4>
<ul data-start="161" data-end="309">
<li data-start="161" data-end="309">Jumper-Kabel (10 cm lang, Männchen-Männchen) werden verwendet, um das Tastenfeld mit dem Arduino Nano R3 zu verbinden (siehe 5.2.1).</li>
</ul>
<h4 data-start="311" data-end="343">2&#x20e3; Anschluss der Kabel</h4>
<ul data-start="344" data-end="622">
<li data-start="344" data-end="420">Achte darauf, dass du die Kabel nicht verdrehst, sondern nur biegst.</li>
<li data-start="421" data-end="522">Das linke Kabel wird an D2 des Arduino angeschlossen, und das rechte Kabel an D8.</li>
<li data-start="523" data-end="622">Insgesamt sind es sieben Kabel, die die Anschlüsse D2 bis D8 belegen (siehe 5.2.3).</li>
</ul>
<h4 data-start="624" data-end="658">3&#x20e3; Pin-Leisten verwenden</h4>
<ul data-start="659" data-end="815">
<li data-start="659" data-end="815">Da das Rastermaß des Arduino-Adapters und des Tastenfelds nicht übereinstimmen, kannst du Pin-Leisten verwenden, um die Verbindung herzustellen.</li>
</ul>
<h4 data-start="817" data-end="857">4&#x20e3; Befestigung der LED-Klemmen</h4>
<ul data-start="858" data-end="1023">
<li data-start="858" data-end="944">Die Klammern (3.0.4) für die WS2812B LEDs werden an den LEDs festgeklickt.</li>
<li data-start="945" data-end="1023">Am besten machst du das auf einer ebenen Oberfläche (siehe 5.2.4).</li>
</ul>
<h4 data-start="1025" data-end="1065">5&#x20e3; Anbringen des LED-Streifens</h4>
<ul data-start="1066" data-end="1214">
<li data-start="1066" data-end="1214">Der WS2812B Streifen wird nun mittig an der Oberseite der Trägerplatte aufgeschoben (siehe 5.2.5 Vorderseite und 5.2.6 Rückseite).</li>
</ul>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1660 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/5-2.jpg" alt="" width="2310" height="1330" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/5-2.jpg 2316w, https://nerd-corner.com/wp-content/uploads/2024/12/5-2-300x173.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/5-2-1024x589.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/5-2-768x442.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/5-2-1536x884.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/5-2-2048x1179.jpg 2048w" sizes="auto, (max-width: 2310px) 100vw, 2310px" /></p>
<h3 data-start="86" data-end="134">Zusammenbau der Trägerplatte und Gehäuse</h3>
<h4 data-start="136" data-end="202">1&#x20e3; Verschrauben der Trägerplatte mit dem Gehäuse (5.3.1)</h4>
<ul data-start="203" data-end="660">
<li data-start="203" data-end="382">Achte darauf, dass du M3 Schrauben verwendest, die nicht zu lang sind. Andernfalls könnten sie beim Anziehen eine Erhöhung in die Vorderseite des Gehäuses drücken.</li>
<li data-start="383" data-end="525">Die Ausrichtung der Trägerplatte im Gehäuse ist wichtig: Der LED-Streifen sollte auf der gleichen Seite wie die LED-Blende sein.</li>
<li data-start="526" data-end="660">Nach dem Verschrauben sollte das Tastenfeld leicht an den inneren Rahmen des Gehäuses gepresst sein (siehe 5.3.2 und 5.3.3).</li>
</ul>
<h4 data-start="662" data-end="727">2&#x20e3; Vormontage der Hohlbuchse für die Notstromversorgung</h4>
<ul data-start="728" data-end="1069">
<li data-start="728" data-end="833">Für die Notstromversorgung musst du die Hohlbuchse vormontieren und an das Gehäuse schrauben.</li>
<li data-start="834" data-end="1069">Die Hohlbuchse mit Brücke und Mutter wird in Bild 5.3.4 dargestellt.
<ul data-start="917" data-end="1069">
<li data-start="917" data-end="984">Stecke die Hohlbuchse in die runde Vertiefung der Brücke.</li>
<li data-start="989" data-end="1066">Kontere die Hohlbuchse mit der Mutter auf der anderen Seite der Brücke.</li>
</ul>
</li>
</ul>
<h4 data-start="1070" data-end="1120">3&#x20e3; Befestigung der Hohlbuchse im Gehäuse</h4>
<ul data-start="1121" data-end="1275">
<li data-start="1121" data-end="1275">Die Hohlbuchse mit der Brücke wird dann an die im Gehäuse vorgesehenen Haltesäulen mit M2 Schrauben verschraubt (siehe 5.3.5 und 5.3.6).</li>
</ul>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1659 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/5-3.jpg" alt="" width="2310" height="1329" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/5-3.jpg 2317w, https://nerd-corner.com/wp-content/uploads/2024/12/5-3-300x173.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/5-3-1024x589.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/5-3-768x442.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/5-3-1536x884.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/5-3-2048x1178.jpg 2048w" sizes="auto, (max-width: 2310px) 100vw, 2310px" /></p>
<h3 data-start="106" data-end="172">Anschluss der Kabel und Fertigstellung der Stromversorgung</h3>
<h4 data-start="174" data-end="232">1&#x20e3; Anschluss des Steuerkabels für den Servomotor</h4>
<ul data-start="233" data-end="335">
<li data-start="233" data-end="335">Das Steuerkabel für den Servomotor wird an Pin D9 des Nano R3 angeschlossen (5.4.1).</li>
</ul>
<h4 data-start="337" data-end="395">2&#x20e3; Anschluss der Stromversorgung für den Nano R3</h4>
<ul data-start="396" data-end="520">
<li data-start="396" data-end="520">Die Stromversorgung des Nano R3 wird an den GND-Pin und den VIN-Pin des Nano R3 angeschlossen (5.4.2).</li>
</ul>
<h4 data-start="522" data-end="577">3&#x20e3; Anschluss des Steuerkabels für den WS2812B</h4>
<ul data-start="578" data-end="691">
<li data-start="578" data-end="691">Das Steuerkabel für den WS2812B LED-Streifen wird an Pin D10 des Nano R3 angeschlossen (5.4.3).</li>
</ul>
<h4 data-start="693" data-end="737">4&#x20e3; Verkabelung des Stromverteilers</h4>
<ul data-start="738" data-end="1010">
<li data-start="738" data-end="1010">Alle restlichen Kabel müssen an den Stromverteiler angeschlossen werden:
<ul data-start="821" data-end="1010">
<li data-start="821" data-end="847">Servomotor-Anschluss</li>
<li data-start="852" data-end="873">Arduino Nano R3</li>
<li data-start="878" data-end="904">WS2812B LED-Streifen</li>
<li data-start="909" data-end="1010">Hohlbuchse Alle Verbindungen erfolgen mit Plus und Minus (siehe 5.4.4 und 5.4.5).</li>
</ul>
</li>
</ul>
<h4 data-start="1012" data-end="1103">5&#x20e3; Verwendung von unterschiedlichen Farben und Anschlussarten für die Verkabelung</h4>
<ul data-start="1104" data-end="1349">
<li data-start="1104" data-end="1222">Es hilft, unterschiedliche Kabel-Farben zu verwenden:
<ul data-start="1168" data-end="1222">
<li data-start="1168" data-end="1190">Rot für Plus</li>
<li data-start="1195" data-end="1222">Schwarz für Minus</li>
</ul>
</li>
<li data-start="1223" data-end="1349">Der Stromverteiler hat unterschiedliche Anschlussarten:
<ul data-start="1289" data-end="1349">
<li data-start="1289" data-end="1316">Männlich für Plus</li>
<li data-start="1321" data-end="1349">Weiblich für Minus</li>
</ul>
</li>
</ul>
<h4 data-start="1351" data-end="1386">6&#x20e3; Hinweis zur Hohlbuchse</h4>
<ul data-start="1387" data-end="1512">
<li data-start="1387" data-end="1512"><a href="https://nerd-corner.com/de/hohlbuchse-als-schalter-verwenden/">Zur Verkabelung und Anschluss der Hohlbuchse gibt es einen eigenen Artikel, den du unbedingt lesen solltest</a></li>
</ul>
<h4 data-start="1514" data-end="1601">7&#x20e3; Anbringen der Stecker-Gehäuse für den Servomotor und die Energieversorgung</h4>
<ul data-start="1602" data-end="1885">
<li data-start="1602" data-end="1680">Für den Servomotor-Anschluss wird ein drei-poliger Stecker benötigt.</li>
<li data-start="1681" data-end="1885">Für die externe Energieversorgung wird ein zwei-poliger Stecker benötigt.
<ul data-start="1767" data-end="1885">
<li data-start="1767" data-end="1885">Weitere Details zum Konstruktionsprozess und den Download-Links für <a href="https://nerd-corner.com/de/3d-gedruckte-dupont-steckverbindung-fuer-jumper-kabel/">Stecker-Gehäuse</a> findest du hier.</li>
</ul>
</li>
</ul>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1658 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/5-4.jpg" alt="" width="2310" height="1330" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/5-4.jpg 2316w, https://nerd-corner.com/wp-content/uploads/2024/12/5-4-300x173.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/5-4-1024x589.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/5-4-768x442.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/5-4-1536x884.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/5-4-2048x1179.jpg 2048w" sizes="auto, (max-width: 2310px) 100vw, 2310px" /></p>
<p>Im Bild 5.5.1 ist nochmal die ganze Verkabelung dargestellt.</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1657 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/5-5.jpg" alt="fritzing keypad lock" width="2310" height="1330" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/5-5.jpg 2316w, https://nerd-corner.com/wp-content/uploads/2024/12/5-5-300x173.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/5-5-1024x589.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/5-5-768x442.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/5-5-1536x884.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/5-5-2048x1179.jpg 2048w" sizes="auto, (max-width: 2310px) 100vw, 2310px" /></p>
<h2>Arduino Code</h2>
<p>Nach der intensiven Arbeit mit der Hardware und dem 3D-Drucker widmen wir uns nun der Strategie und der Programmierung des Tastenfeldschlosses. Warum ist eine Strategie für ein Tastenfeldschloss wichtig? Eine durchdachte Handlungsabfolge – also die Reihenfolge, in der das Tastenfeldschloss bedient wird, welche Ereignisse sich aus bestimmten Aktionen ergeben und welches Ziel verfolgt wird – bildet die Grundlage für eine funktionale Steuerung. Dieser Leitfaden ist daher auch entscheidend für die Programmierung des Tastenfeldschlosses: Er legt fest, was wann passieren soll und welche Hardware dabei zum Einsatz kommt. Ziel ist es, eine logische und nachvollziehbare Abfolge von Ereignissen zu schaffen, die in diesem Fall auch optisch nachzuvollziehen ist.</p>
<p>Beispiel 1:<br />
Das Tastenfeldschloss soll anzeigen, wie viele Stellen bereits eingegeben wurden. (Siehe Bilder 7.0.1 bis 7.0.4)</p>
<p>Beispiel 2:<br />
Das Tastenfeldschloss soll nach Betätigung einer bestimmten Taste mitteilen, ob das eingegebene Kennwort korrekt ist. (Siehe Bild 7.0.5)</p>
<p>Beispiel 3:<br />
Das Tastenfeldschloss soll anzeigen, wenn etwas falsch eingegeben wurde, wie etwa ein falsches Kennwort oder zu viele Tastenanschläge. (Siehe Bild 7.0.8)</p>
<p>Beispiel 4:<br />
Das Tastenfeldschloss soll den aktuellen Zustand anzeigen. (Siehe Bilder 7.0.5 bis 7.0.7)</p>
<p>Kommen wir nun zur Programmierung des Tastenfeldschlosses, die sich in einigen Punkten von herkömmlichen Programmen im Internet unterscheidet. Zu Beginn werden die drei notwendigen Bibliotheken eingebunden: &lt;Keypad.h&gt; für das Tastenfeld, &lt;Adafruit_NeoPixel.h&gt; für die WS2812B LEDs und &lt;Servo.h&gt; für den Servomotor (Bild 6.0.1). Im folgenden Abschnitt wird die Pin-Belegung für die LEDs festgelegt, wobei Pin D10 verwendet wird und die Anzahl der LEDs sowie das Farbschema bestimmt werden. Ebenso wird die Helligkeit der LEDs festgelegt – dieser Wert kann je nach Standort angepasst werden, wobei höhere Werte für mehr Helligkeit sorgen (Werte von 0 bis 255). (Siehe Bild 6.0.2) Der dritte Abschnitt widmet sich der Beschreibung des verwendeten Tastenfelds, einschließlich der Anzahl der Reihen und Spalten sowie der Belegung der Tasten. (Siehe Bild 6.0.3)</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1656 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/6-0-1.jpg" alt="" width="2310" height="1330" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/6-0-1.jpg 2316w, https://nerd-corner.com/wp-content/uploads/2024/12/6-0-1-300x173.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/6-0-1-1024x589.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/6-0-1-768x442.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/6-0-1-1536x884.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/6-0-1-2048x1179.jpg 2048w" sizes="auto, (max-width: 2310px) 100vw, 2310px" /></p>
<p>Im vierten Abschnitt wird der Servomotor konfiguriert, indem der Gradbereich festgelegt wird, den er zurücklegen kann, sowie die Geschwindigkeit, mit der er sich bewegen soll (siehe Bild 6.1.1). Anschließend folgt der Abschnitt zur Kennworteingabe. Hier habt ihr die Möglichkeit, das Standardkennwort 1516 zu ändern, um ein neues vierstelliges Kennwort festzulegen. Das Programm funktioniert nur korrekt, wenn ein vierstelliger Code eingegeben wird. In diesem Abschnitt wird außerdem der Steuer-Pin für den Servomotor auf D9 gesetzt (siehe Bild 6.1.2). Der darauf folgende Abschnitt widmet sich der Festlegung der Farben für die verschiedenen Ereignisse.</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1655 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/6-1-1.jpg" alt="" width="2310" height="1330" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/6-1-1.jpg 2316w, https://nerd-corner.com/wp-content/uploads/2024/12/6-1-1-300x173.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/6-1-1-1024x589.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/6-1-1-768x442.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/6-1-1-1536x884.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/6-1-1-2048x1179.jpg 2048w" sizes="auto, (max-width: 2310px) 100vw, 2310px" /></p>
<p>In den letzten beiden Abschnitten wird das Verhalten des Tastenfeldschlosses bei bestimmten Handlungen erläutert. Diese Beschreibung stellt natürlich nur einen groben Überblick über das Programm dar. In einem zukünftigen Artikel für Tastenfeldschloss Version 2 werden wir dann das Programm detaillierter und umfassender erklären.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">//==========================================Librarys==============================================================
#include &lt;Keypad.h&gt;
#include &lt;Adafruit_NeoPixel.h&gt;
#include &lt;Servo.h&gt;
//========================================Neo-Pixel==============================================================
#define LED_PIN 10
#define LED_COUNT 3
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
int led_strength = 75; //controlls Brighttness (0 - 255)
//========================================Keypad=================================================================
const byte rows = 4;
const byte cols = 3;
char keys[rows][cols] = {
 {'1', '2', '3'},
 {'4', '5', '6'},
 {'7', '8', '9'},
 {'*', '0', '#'}
};
byte rowPins[rows] = {8, 7, 6, 5};
byte colPins[cols] = {4, 3, 2};
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, rows, cols );
//=========================================Servo============================================================
Servo lock;
int pos = 0;
int servo_angle = 180;
int servo_speed = 15;
//======================================Password===============================================================
String input;
const String password = "1516"; //Set Password
int n = 1;

 

void setup() {
 input.reserve(password.length() +2);
 strip.begin();
 strip.show();
 lock.attach(9); //motor pin
}

void loop() {
//-------------------colours------------------------------------------------------------------- 
 uint32_t blue = strip.Color(0, 0, led_strength);
 uint32_t green = strip.Color(0, led_strength, 0);
 uint32_t red = strip.Color(led_strength, 0, 0);
 uint32_t orange = strip.Color(led_strength, led_strength/2, 0);

 char key = keypad.getKey();

if (key != NO_KEY) {
//-------------------------End conditions------------------------------------------------------
 if (key == '#') {
 if (input == password) {
 //unlock
 strip.clear();
 strip.fill(green, 0, LED_COUNT);
 strip.show();
 for (pos = 0; pos &lt;= servo_angle; pos += 1) {
 lock.write(pos);
 delay(servo_speed );
 }
 while (1 == 1)
 { char key = keypad.getKey();
 strip.clear();
 strip.fill(orange, 0, LED_COUNT);
 strip.show();
 if (key == '*')
 {
 strip.clear();
 strip.fill(green, 0, LED_COUNT);
 strip.show();
 break;
 }
 }

 for (pos = servo_angle; pos &gt;= 0; pos -= 1) {
 lock.write(pos);
 delay(servo_speed );
 }
 n = 1;
 input = "";
 delay (1000);
 strip.clear();
 strip.show();
 } else {
 //wrong password
 strip.clear();
 strip.fill(red, 0, LED_COUNT);
 strip.show();
 n = 1;
 input = "";
 delay (1000);
 strip.clear();
 strip.show();
 }
 }
 else if (n == password.length() + 1) {
 //Input too long
 strip.clear();
 strip.fill(red, 0, LED_COUNT);
 strip.show();
 n = 1;
 input = "";
 delay (1000);
 strip.clear();
 strip.show();
 }
//----------------------------------Buttons------------------------------------------
 else {
 input += key;
 if (n == password.length() ) {
 strip.clear();
 strip.fill(blue, 0, LED_COUNT); 
 strip.show();
 n++;
 }
 else {

 strip.clear();
 strip.setPixelColor(n-1, blue);
 strip.show();
 n++;
 }
 }
 }

}</pre>
<h2 data-start="14" data-end="438">Funktion</h2>
<p data-start="14" data-end="438">Die Bedienung des Tastenfeldschlosses ist sehr intuitiv. Sobald die erste Taste gedrückt wird, leuchtet die rechte LED blau (7.0.1). Beim zweiten Tastendruck wird die mittlere LED blau (7.0.2). Nach der dritten Taste strahlt die linke LED in Blau (7.0.3). Mit der vierten Taste leuchten schließlich alle drei LEDs blau (7.0.4). Wenn diese drei LEDs in Blau leuchten, weiß der Benutzer, dass die #-Taste gedrückt werden muss.</p>
<p data-start="440" data-end="897">Wird die #-Taste betätigt, erfolgt die Überprüfung des Passworts. Ist das Passwort korrekt, leuchten alle drei LEDs gleichzeitig grün, und der Servomotor wird aktiviert (7.0.5). Das grüne Licht bleibt so lange an, wie der Servomotor seine Endlage (offen) noch nicht erreicht hat. Sobald der Servomotor die Endlage (offen) erreicht, wechseln die LEDs von Grün zu Orange (7.0.6). Die orange Farbe bleibt so lange erhalten, bis der Benutzer die *-Taste drückt.</p>
<p data-start="899" data-end="1152">Nach dem Drücken der *-Taste wechseln die LEDs wieder auf Grün (7.0.7) und bleiben in dieser Farbe, bis der Servomotor die Endlage (geschlossen) erreicht hat. Ist dies der Fall, erlöschen die LEDs, und das Tastenfeldschloss ist bereit für neue Eingaben.</p>
<p data-start="1154" data-end="1383" data-is-last-node="">Falls jedoch das Passwort nach dem Drücken der #-Taste in Schritt 7.0.4 falsch ist, leuchten alle drei LEDs rot (7.0.8). Ebenso wird das rote Licht angezeigt, wenn mehr als vier Tasten, abgesehen von der #-Taste, gedrückt werden.</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1654 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/7-0-1.jpg" alt="keypad lock step by step guide" width="2310" height="1329" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/7-0-1.jpg 2317w, https://nerd-corner.com/wp-content/uploads/2024/12/7-0-1-300x173.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/7-0-1-1024x589.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/7-0-1-768x442.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/7-0-1-1536x884.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/7-0-1-2048x1178.jpg 2048w" sizes="auto, (max-width: 2310px) 100vw, 2310px" /></p>
<h2>Türmontage</h2>
<p>Natürlich ist diese Variante nicht für den Einsatz an einer gewöhnlichen Tür gedacht, bei der man einfach hindurchgeht und die Tür sich von selbst wieder schließt. Der Grund dafür liegt im offenbleiben des Schlosses, bis die *-Taste gedrückt wird. Doch wie kann man die *-Taste drücken, wenn man sich auf der anderen Seite des Tastenfeldschlosses befindet, hinter der Wand? Eine Verzögerung im Programm könnte zwar helfen, aber wer weiß schon, wie lange man braucht, um die Tür zu passieren und hinter sich zu schließen? Eine viel sinnvollere Lösung wäre es, einen zusätzlichen Schalter zu implementieren, der auf der anderen Seite der Tür angebracht wird. Dieser Schalter würde, ohne eine Passworteingabe, bei Betätigung direkt zu Punkt 7.0.6 führen und das Schloss öffnen, sodass die Tür von innen geöffnet werden kann.</p>
<p>Doch das ist Zukunftsmusik. Nun kommen wir zur Montage des Tastenfeldschlosses an einer Tür. Hierfür habe ich auf der Rückseite des Gehäuses vier Bohrungen vorgesehen. Diese Bohrungen haben an der Innenseite eine Vertiefung für herkömmliche Sechskantmuttern M4 DIN 934. Die entsprechenden Bohrungen sind in Bild 8.0.1 (Rückansicht) und Bild 8.0.2 (Vorderansicht) zu sehen, wobei das Gehäuse zur besseren Anschauung etwas transparent dargestellt ist. Die Installation der Muttern ist denkbar einfach: Nachdem sichergestellt wurde, dass sämtliches Supportmaterial entfernt ist, wird die Mutter von hinten in die vorgesehene Vertiefung gedrückt. Es sollten keine Einpressmuttern verwendet werden, da diese unnötig teuer sind.</p>
<p>Für die Befestigung an der Wand oder Tür habe ich eine Bohrschablone entwickelt (Bild 8.0.3). Diese Schablone enthält an den vier Ecken jeweils eine Bohrung mit einem Durchmesser von 4 mm. Der Abstand der Bohrungen entspricht dem Abstand der Bohrungen am Gehäuse. Man kann die Schablone bequem an der Oberfläche der Tür fixieren, zum Beispiel mit Klebeband (Bild 8.0.4). So spart man sich das mühsame Anreißen der Bohrlöcher und vermeidet Fehler.</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1653 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/8-0-1.jpg" alt="" width="2310" height="1330" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/8-0-1.jpg 2316w, https://nerd-corner.com/wp-content/uploads/2024/12/8-0-1-300x173.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/8-0-1-1024x589.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/8-0-1-768x442.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/8-0-1-1536x884.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/8-0-1-2048x1179.jpg 2048w" sizes="auto, (max-width: 2310px) 100vw, 2310px" /></p>
<p>Auf Bild 8.1.1 sehen wir die gebohrten Montagelöcher von der Vorderseite, markiert durch magentafarbene Kreise. Dank der Bohrschablone haben diese Löcher den richtigen Abstand zueinander. Falls beim Bohren die Position leicht verrutscht oder nicht ganz exakt eingehalten wurde, stellt das kein Problem dar. In diesem Fall kann man die Montagelöcher einfach etwas größer bohren, um das Tastenfeldschloss beim Anschrauben präzise auszurichten. Zusätzlich sind auf diesem Bild in gelben Kreisen zwei Bohrungen zu sehen, die leider etwas am Rand ausgebrochen sind. Diese Bohrungen sind für das Kabel des Servos und die Energieversorgung vorgesehen. Nachdem die Bohrungen fertiggestellt waren, habe ich das Tastenfeldschloss angebracht und die Kabel zuvor durch die vorgesehenen Bohrungen auf der Rückseite der Tür (Bild 8.1.2) geführt.</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1652 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/8-1.jpg" alt="" width="2310" height="1330" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/8-1.jpg 2316w, https://nerd-corner.com/wp-content/uploads/2024/12/8-1-300x173.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/8-1-1024x589.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/8-1-768x442.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/8-1-1536x884.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/8-1-2048x1179.jpg 2048w" sizes="auto, (max-width: 2310px) 100vw, 2310px" /></p>
<p>Die gesamte Montagearbeit des Tastenfeldschlosses ist auf der Vorderseite der Tür in Bild 8.2.1 zu sehen. Wer mit dieser Montagelösung nicht zufrieden ist, kann ich beruhigen: Als letzten Schritt habe ich einen Rückendeckel entworfen, der es ermöglicht, das Tastenfeldschloss an einer Wand oder einem Rahmen zu befestigen (Bild 8.2.2). Dazu steckt man einfach das Gehäuse des Tastenfeldschlosses auf den Rückendeckel und verschraubt es von hinten mit M4 Senkkopfschrauben (markiert durch magentafarbene Kreise in Bild 8.2.2). Danach wird das ganze Konstrukt an der Wand befestigt, wofür zwei Laschen mit jeweils zwei Bohrungen zur Verfügung stehen (gelbe Kreise in Bild 8.2.2).</p>
<p>Nicht zu vergessen ist, dass man am Gehäuse zusätzlich eine oder mehrere Bohrungen für die Kabel des Servos und der Energieversorgung anbringen muss. Dabei sollte man auch das Sicherheitsrisiko bedenken, dass eine unbefugte Person das Tastenfeldschloss über die zugänglichen Schrauben abschrauben könnte.</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1651 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/8-2.jpg" alt="" width="2310" height="1330" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/8-2.jpg 2316w, https://nerd-corner.com/wp-content/uploads/2024/12/8-2-300x173.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/8-2-1024x589.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/8-2-768x442.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/8-2-1536x884.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/8-2-2048x1179.jpg 2048w" sizes="auto, (max-width: 2310px) 100vw, 2310px" /></p>
<h2>Schließmechanismus</h2>
<p>Wer jetzt noch einen echten Schließmechanismus vermisst, der nicht nur einen Servo-Anschluss nutzt, kann sich auf den „Schließer“ freuen, der sich bereits in der Entwicklung befindet und in den kommenden Versionen des Tastenfeldschlosses präsentiert wird.</p>
<p>Die Entwicklung des Tastenfeldschlosses war eine spannende, aber auch herausfordernde Aufgabe. Besonders hervorzuheben sind einige Eigenschaften der VERSION 1 des Tastenfeldschlosses:</p>
<ul>
<li>Verschiedene Arduino Nano-Modelle und Adapter können verwendet werden – Flexibilität bei der Hardwarewahl.</li>
<li>Optisches Feedback – Über die Zahleneingabe und den Status des Tastenfeldschlosses wird der Benutzer direkt informiert.</li>
<li>Notstromversorgung über Hohlbuchse – Garantiert zuverlässige Funktion im Falle von Stromausfällen.</li>
<li>Stabiles Programm – Die Software wurde so entwickelt, dass sie zuverlässig funktioniert.</li>
<li>Optimale Haptik für Links- und Rechtshänder – Das Tastenfeldschloss wurde ergonomisch gestaltet, um allen Nutzern gerecht zu werden.</li>
</ul>
<h2>Dateien zum Herunterladen</h2>
<ul>
<li><a href="https://www.thingiverse.com/thing:6840254">Tastenfeldschloss Gehäuse</a></li>
</ul>
<p>The post <a href="https://nerd-corner.com/de/diy-tastenfeldschloss-3d-druck-und-programmierung/">DIY Tastenfeldschloss &#8211; 3D Druck und Programmierung</a> appeared first on <a href="https://nerd-corner.com/de">Nerd Corner</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://nerd-corner.com/de/diy-tastenfeldschloss-3d-druck-und-programmierung/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Erfahrungen aus der Praxis: NestJS auf Vercel hosten</title>
		<link>https://nerd-corner.com/de/erfahrungen-aus-der-praxis-nestjs-auf-vercel-hosten/</link>
					<comments>https://nerd-corner.com/de/erfahrungen-aus-der-praxis-nestjs-auf-vercel-hosten/#respond</comments>
		
		<dc:creator><![CDATA[Nerds]]></dc:creator>
		<pubDate>Thu, 14 Nov 2024 07:55:27 +0000</pubDate>
				<category><![CDATA[Software-DE]]></category>
		<category><![CDATA[angular]]></category>
		<category><![CDATA[Anleitung]]></category>
		<category><![CDATA[cookie-sessions]]></category>
		<category><![CDATA[CORS Fehler]]></category>
		<category><![CDATA[Cors Fehler nest]]></category>
		<category><![CDATA[Datenbank]]></category>
		<category><![CDATA[Datenbank hosten]]></category>
		<category><![CDATA[Datenbank konfigurieren]]></category>
		<category><![CDATA[Einstellungen]]></category>
		<category><![CDATA[Erfahrungsbericht]]></category>
		<category><![CDATA[express]]></category>
		<category><![CDATA[express-sessions]]></category>
		<category><![CDATA[express.js]]></category>
		<category><![CDATA[google cloud]]></category>
		<category><![CDATA[google cloud mysql]]></category>
		<category><![CDATA[Hosten]]></category>
		<category><![CDATA[internal Server Fehler Vercel]]></category>
		<category><![CDATA[Internal Server Fehler Vercel nest]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[JSON]]></category>
		<category><![CDATA[mySql]]></category>
		<category><![CDATA[Nest]]></category>
		<category><![CDATA[NestJs]]></category>
		<category><![CDATA[Redis]]></category>
		<category><![CDATA[redis session management]]></category>
		<category><![CDATA[redis session management nest.js]]></category>
		<category><![CDATA[Schritt für Schritt Anweisung]]></category>
		<category><![CDATA[Server]]></category>
		<category><![CDATA[Session Mamagement]]></category>
		<category><![CDATA[session management]]></category>
		<category><![CDATA[typescript]]></category>
		<category><![CDATA[Vercel]]></category>
		<category><![CDATA[vercel.json]]></category>
		<category><![CDATA[Visual Studio]]></category>
		<guid isPermaLink="false">https://nerd-corner.com/?p=1632</guid>

					<description><![CDATA[<p>Nachdem ich Stunden damit verbracht habe, meine NestJS-App auf Vercel zu hosten und es nicht zum Laufen brachte, dachte ich mir, dass es an der &#8230; </p>
<p>The post <a href="https://nerd-corner.com/de/erfahrungen-aus-der-praxis-nestjs-auf-vercel-hosten/">Erfahrungen aus der Praxis: NestJS auf Vercel hosten</a> appeared first on <a href="https://nerd-corner.com/de">Nerd Corner</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Nachdem ich Stunden damit verbracht habe, meine NestJS-App auf Vercel zu hosten und es nicht zum Laufen brachte, dachte ich mir, dass es an der Zeit ist, zu dokumentieren, was ich gelernt habe &#8211; nicht nur, um mir in Zukunft Zeit zu sparen, sondern hoffentlich auch, um anderen zu helfen, einige der Fallstricke zu vermeiden, in die ich geraten bin. Hier ist eine Aufschlüsselung dessen, was funktioniert hat, was nicht, und wie ich es schließlich geschafft habe, dass alles reibungslos läuft.</p>
<p><em><strong>Das könnte dich ebenfalls interessieren:</strong> <a href="https://nerd-corner.com/de/swagger-api-doku-zu-node-server-hinzufuegen/" target="_blank" rel="noopener">Swagger zu Node Server hinzufügen</a></em></p>
<h3>Schritt 1: NestJS auf Vercel hosten</h3>
<p>Das Wichtigste zuerst: Die Grundeinstellungen für die Bereitstellung auf Vercel. Vercel ist großartig für Serverless, aber die Arbeit mit NestJS benötigt ein paar Anpassungen. Die Hauptsache ist, eine vercel.json-Konfigurationsdatei einzurichten, die Vercel genau sagt, wie die App zu behandeln ist.</p>
<p>Hier ist die Endkonfiguration:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="json" data-enlighter-theme="beyond">{
  "version": 2,
  "builds": [
    {
      "src": "src/main.ts",
      "use": "@vercel/node"
    }
  ],
  "routes": [
    {
      "src": "/(.*)",
      "dest": "src/main.ts",
      "methods": [
        "GET",
        "POST",
        "PUT",
        "PATCH",
        "OPTIONS",
        "DELETE",
        "HEAD",
        "CONNECT",
        "TRACE"
      ]
    }
  ]
}
</pre>
<p>Ich habe daraufhin in Vercel folgende Fehlermeldung erhalten:</p>
<pre>This Serverless Function has crashed. Your connection is working correctly. 
Vercel is working correctly. 500: INTERNAL_SERVER_ERROR 
Code: FUNCTION_INVOCATION_FAILED ID: bom1::sgk4v-1711014022883-1e9ed54f4c37</pre>
<p>Bei einem Blick in die Logs stellte ich fest, dass die Datenbankverbindung ein Problem darstellte und außerdem die folgende Logmeldung angezeigt wurde:</p>
<pre>No exports found in module "/var/task/app-name/src/main.js".
Did you forget to export a function or a server?</pre>
<p>Es stellte sich heraus, dass ich den zweiten Teil der Fehlermeldung ignorieren und mich nur auf die Datenbankverbindung konzentrieren musste.</p>
<h3>Schritt 2: Konfigurieren der Datenbank</h3>
<p>Für meine Anwendung habe ich eine mysql-Datenbank mit mehreren Schemata verwendet. Ich habe mehrere kostenlose Angebote ausprobiert, aber sie waren nicht mit dem Ansatz mehrerer Schemata kompatibel. Daher habe ich mich für das Hosting bei Google Cloud entschieden. Ich habe es auf einen Preis von 0,01 $ pro Stunde heruntergeschraubt und das 300 $-Einsteigerangebot genutzt.</p>
<p>Um Vercel eine Verbindung zu ermöglichen, musste in der Konfiguration von Google Cloud die IP-Adresse auf <strong>0.0.0.0/0</strong> gesetzt werden, so dass die Datenbank von jeder IP-Adresse aus zugänglich war.</p>
<h3>Schritt 3: Umgang mit CORS</h3>
<p>Die CORS Fehler haben auch für einige Kopfschmerzen gesorgt. Stelle sicher, dass OPTIONS für CORS Preflight-Anfragen zugelassen wird, da Vercel eine explizite Erlaubnis für Cross-Origin-Anfragen benötigt. Am Ende musste ich eine Menge Header hinzufügen, um sicherzustellen, dass die Anfragen erlaubt waren:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="beyond">app.enableCors({
    origin: 'domain-name',
    credentials: true,
    methods: 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS',
    allowedHeaders: [
      'Origin',
      'X-Requested-With',
      'Content-Type',
      'Accept',
      'Authorization',
    ],
  });</pre>
<h3>Schritt 4: Umstellung auf express-session und Redis für die Sitzungsverwaltung</h3>
<p>Einer der kniffligsten Teile war die Sessions zum Laufen zu bringen. Ich begann mit der <em>cookie-session</em> Bibliothek, aber Vercel ignoriert sie komplett. Nach einem Blick in die Dokumentation und einigem Ausprobieren wechselte ich zur <em>express-session</em> Bibliothek, da sie populärer ist und besser mit Vercels serverloser Umgebung funktioniert.</p>
<p>Aus irgendeinem Grund muss die Import-Syntax genau so aussehen:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="beyond">import session = require('express-session');</pre>
<p>Außerdem musste ich die Sitzungsmiddleware mit aktiviertem Vertrauensproxy konfigurieren, da Vercel Anfragen proxifiziert. So sieht die Einstellung aus:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="beyond">const expressApp = app.getHttpAdapter().getInstance();
expressApp.set('trust proxy', true);</pre>
<p>Auch die Einstellung secure: true und sameSite: &#8217;none&#8216; war wichtig, um sicherzustellen, dass Cookies über HTTPS und verschiedene Domainanfragen funktionieren!</p>
<p>Beachten, dass mit Vercel mehrere serverlose Instanzen Anfragen bearbeiten können, was bei parallelen Anfragen leider zu Sitzungskonflikten führt. Um dies zu beheben, habe ich meinen Sitzungsspeicher mit einer Redis-Instanz verbunden. Glücklicherweise war das super einfach.</p>
<p><a href="https://redis.io/">Redis</a> hält die Sitzungsdaten konsistent und vermeidet Konflikte zwischen Anfragen, insbesondere unter Last. Hier der Code:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="beyond">const expressApp = app.getHttpAdapter().getInstance();
expressApp.set('trust proxy', true);

const redisClient = createClient({
    password: process.env.REDIS_PASSWORD,
    socket: {
      host: process.env.REDIS_HOST,
      port: parseInt(process.env.REDIS_PORT, 10),
    },
  });

  redisClient
    .connect()
    .catch((err) =&gt;
      console.log('Could not establish a connection with Redis: ' + err),
    );

  redisClient.on('error', (err) =&gt; console.log('Redis error: ' + err));
  redisClient.on('connect', () =&gt;
    console.log('Connected to Redis successfully'),
  );

  app.use(
    session({
      store: new RedisStore({ client: redisClient }),
      secret: process.env.COOKIE_SECRET,
      cookie: {
        secure: process.env.NODE_ENV !== 'development',
        sameSite: process.env.NODE_ENV === 'development' ? 'lax' : 'none',
        maxAge: 86400000,
      },
    }),
  );</pre>
<h3>Schritt 5: Hinzufügen von withCredentials im Frontend</h3>
<p>Dieser Schritt ist nur eine Randnotiz: Damit Session-Cookies zwischen Frontend und Backend funktionieren, muss withCredentials in den HTTP-Anfragen meines Frontends auf true gesetzt werden. Dies ermöglicht die Einbeziehung von Cookies in herkunftsübergreifende Anfragen, was wichtig ist, wenn das Frontend und das Backend getrennt gehostet werden. Ich musste sicherstellen, dass der HTTP-Client von Angular diese Einstellung aktiviert hat.</p>
<h3>Schritt 6: Schriftart hinzufügen</h3>
<p>Um Schriftdateien in Ihr NestJS-Projekt einzubinden, muss man die CompilerOptions in der nest-cli.json-Datei verwenden, um Assets für die Build-Ausgabe zu definieren, z. B. durch die Angabe von „include“: „**/*.ttf“ und ‚outDir‘: „dist/src“.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="json" data-enlighter-title="nest-cli.json" data-enlighter-theme="beyond">{
  "$schema": "https://json.schemastore.org/nest-cli",
  "collection": "@nestjs/schematics",
  "sourceRoot": "src",
  "compilerOptions": {
    "assets": [
      {
        "include": "**/*.ttf",
        "outDir": "dist/src"
      }
    ],
    "deleteOutDir": true
  }
}</pre>
<p>Anschließend kann man nach dem Build die Schriftarten im Code mit path.resolve referenzieren, z. B. path.resolve(__dirname, &#8218;../fonts/Roboto-Regular.ttf&#8216;). Auf diese Weise wird sichergestellt, dass die Schriftartendateien mit dem Build gebündelt werden und während der Laufzeit zugänglich sind.</p>
<h3>Schlussgedanken zum Thema NestJS auf Vercel hosten</h3>
<p>Das Deployment meiner NestJS-App auf Vercel war eine wahre Achterbahnfahrt. Manchmal hatte ich das Gefühl, dass ich kurz davor war, alles perfekt zum Laufen zu bringen, nur um dann mit neuen Fehlern konfrontiert zu werden, die mich zurück in den Fehlersuch-Modus schickten. Es gab Momente der Frustration &#8211; vor allem im Zusammenhang mit der Sessionverarbeitung und CORS-Problemen. Aber jede Lösung brachte ein neues Hoch, und jeder behobene Fehler fühlte sich wie ein kleiner Sieg an.</p>
<p>Jetzt, wo endlich alles reibungslos funktioniert, kann ich sagen, dass es ein großartiges Gefühl ist. Wenn ich sehe, dass meine Anwendung so funktioniert, wie ich es mir vorgestellt habe, ist das all die Kopfschmerzen wert. Es ist eine große Erleichterung, aber noch mehr freut es mich, zu wissen, dass ich jede Hürde überwunden habe und auf das, was ich gelernt habe, zurückblicken kann. Ich hoffe, dieser Leitfaden kann anderen einige dieser Schwierigkeiten ersparen und helfen, diesen „Es funktioniert einfach“-Moment etwas schneller zu erreichen!</p>
<p>The post <a href="https://nerd-corner.com/de/erfahrungen-aus-der-praxis-nestjs-auf-vercel-hosten/">Erfahrungen aus der Praxis: NestJS auf Vercel hosten</a> appeared first on <a href="https://nerd-corner.com/de">Nerd Corner</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://nerd-corner.com/de/erfahrungen-aus-der-praxis-nestjs-auf-vercel-hosten/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>WeMos D1 R2 &#8211; Ganze Webseite mit html, css &#038; js hosten</title>
		<link>https://nerd-corner.com/de/wemos-d1-r2-ganze-webseite-mit-html-css-js-hosten/</link>
					<comments>https://nerd-corner.com/de/wemos-d1-r2-ganze-webseite-mit-html-css-js-hosten/#respond</comments>
		
		<dc:creator><![CDATA[Nerds]]></dc:creator>
		<pubDate>Sat, 02 Mar 2024 23:11:48 +0000</pubDate>
				<category><![CDATA[Arduino Projekte]]></category>
		<category><![CDATA[DIY]]></category>
		<category><![CDATA[Software-DE]]></category>
		<category><![CDATA[Arduino]]></category>
		<category><![CDATA[Arduino mit WLAN]]></category>
		<category><![CDATA[C Programmierung]]></category>
		<category><![CDATA[Datenübertragung]]></category>
		<category><![CDATA[ESP8266]]></category>
		<category><![CDATA[ESP8266 mit mDNS]]></category>
		<category><![CDATA[ganze Webseite]]></category>
		<category><![CDATA[Hardware]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[JS]]></category>
		<category><![CDATA[Klemmsystem]]></category>
		<category><![CDATA[Kommunikationstechnik]]></category>
		<category><![CDATA[led]]></category>
		<category><![CDATA[Lokales Netzwerk]]></category>
		<category><![CDATA[Netzwerk]]></category>
		<category><![CDATA[Schritt für Schritt Anweisung]]></category>
		<category><![CDATA[Webseite hosten]]></category>
		<category><![CDATA[Webserver]]></category>
		<category><![CDATA[WeMos]]></category>
		<category><![CDATA[WeMos D1]]></category>
		<category><![CDATA[WeMos D1 in Arduino IDE]]></category>
		<category><![CDATA[WeMos D1 mini]]></category>
		<category><![CDATA[WeMos D1 R2]]></category>
		<category><![CDATA[WeMOS D1 Webserver]]></category>
		<category><![CDATA[WIFI]]></category>
		<guid isPermaLink="false">https://nerd-corner.com/?p=1526</guid>

					<description><![CDATA[<p>Im ersten Teil der WeMos Reihe wurde die Einrichtung und Wlan Integration erklärt. Darauf aufbauend wird in diesem Artikel Schritt für Schritt beschrieben wie man &#8230; </p>
<p>The post <a href="https://nerd-corner.com/de/wemos-d1-r2-ganze-webseite-mit-html-css-js-hosten/">WeMos D1 R2 &#8211; Ganze Webseite mit html, css &#038; js hosten</a> appeared first on <a href="https://nerd-corner.com/de">Nerd Corner</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Im <a href="https://nerd-corner.com/de/wemos-d1-r2-erste-schritte-und-wlan-integration/">ersten Teil</a> der WeMos Reihe wurde die Einrichtung und Wlan Integration erklärt. Darauf aufbauend wird in diesem Artikel Schritt für Schritt beschrieben wie man eine komplette Webseite mit HTML Seiten, CSS Styling und JavaScript Funktionen auf einem WeMos hosten kann. Das ganze ist ziemlich einfach und unglaublich hilfreich! Ich kann es selbst kaum fassen wie schwer es ist eine vernünftige Anleitung für diese wichtige Funktion im Internet zu finden.</p>
<p>Ich sehe leider sehr oft Blogartikel in denen der HTML Code in der Arduino Datei eingebettet wird. Sowas kann man für eine Mini Demonstration zwar machen, ist aber im Alltag völliger Schwachsinn. Es ist viel zu unübersichtlich und sobald das Projekt wächst nicht mehr nutzbar.</p>
<p>Die ordentliche Alternative ist einen Ordner namens &#8222;data&#8220; einzurichten und in diesem die Webseiten als html Dateien abzulegen. Zusätzlich wird das Styling als CSS Datei gespeichert und es können sogar Funktionen per JavaScript Datei ausgeführt werden. Also alles 1:1 wie auf einem gewöhnlichem Webserver!</p>
<p><em><strong>Das könnte dich auch interessieren: </strong><a href="https://nerd-corner.com/de/wemos-d1-r2-erste-schritte-und-wlan-integration/">WeMos D1 R2 erste Schritte und Wlan Integration</a></em></p>
<h2>Liste der Komponenten</h2>
<ul>
<li>Arduino IDE (Entwicklungsumgebung)</li>
<li><a href="https://amzn.to/3tQPCjC" target="_blank" rel="noopener">WeMos D1 R2</a></li>
</ul>
<h3>Das Einrichten des Dateisystems (offiziell SPIFFS) geschieht einmalig und ist kinderleicht:</h3>
<p><a href="https://nerd-corner.com/de/wemos-d1-r2-erste-schritte-und-wlan-integration/">(Zunächst sollte man die grundlegende Einrichtung aus dem ersten Teil abgeschlossen haben!)</a></p>
<ol>
<li>Auf <a href="https://github.com/esp8266/arduino-esp8266fs-plugin/releases/tag/0.2.0">GitHub</a> eine Kopie der Datei &#8222;ESP8266FS-0.2.0.zip&#8220; herunterladen und entpacken</li>
<li>Die Datei esp8266fs.jar im Arduino-Tool-Verzeichnis ablegen. Der Pfad sieht etwa so aus: [home_dir]\Arduino\tools\ESP8266FS\tool\esp8266fs.jar (Siehe Bild) Ich musste den Pfadteil tools\ESP8266FS\tool\ im Arduino Ordner selbst erstellen. <img loading="lazy" decoding="async" class="zoooom aligncenter wp-image-1531" src="https://nerd-corner.com/wp-content/uploads/2024/03/Screenshot-file-path-esp8266fs.jar-.png" alt="Screenshot file path esp8266fs.jar" width="1080" height="335" srcset="https://nerd-corner.com/wp-content/uploads/2024/03/Screenshot-file-path-esp8266fs.jar-.png 1190w, https://nerd-corner.com/wp-content/uploads/2024/03/Screenshot-file-path-esp8266fs.jar--300x93.png 300w, https://nerd-corner.com/wp-content/uploads/2024/03/Screenshot-file-path-esp8266fs.jar--1024x318.png 1024w, https://nerd-corner.com/wp-content/uploads/2024/03/Screenshot-file-path-esp8266fs.jar--768x238.png 768w" sizes="auto, (max-width: 1080px) 100vw, 1080px" /></li>
<li>Die Arduino IDE neustarten.</li>
</ol>
<p>Das wars auch schon! Man kann jetzt in der Arduino DIE unter Tools den neuen Punkt „ESP8266 Sketch Data Upload“ sehen.</p>
<h2>Wie kann man das neue Dateisystem jetzt nutzen?</h2>
<ol>
<li>Erstellen Sie in ihrem aktuellen WeMos Projekt Ordner einen zusätzlichen Ordner mit dem Namen „data“. So wie in dem nachfolgendem Bild</li>
</ol>
<p><img loading="lazy" decoding="async" class="zoooom aligncenter wp-image-1528" src="https://nerd-corner.com/wp-content/uploads/2024/03/Wemos-data-folder.png" alt="Wemos data folder" width="1040" height="324" srcset="https://nerd-corner.com/wp-content/uploads/2024/03/Wemos-data-folder.png 1146w, https://nerd-corner.com/wp-content/uploads/2024/03/Wemos-data-folder-300x93.png 300w, https://nerd-corner.com/wp-content/uploads/2024/03/Wemos-data-folder-1024x319.png 1024w, https://nerd-corner.com/wp-content/uploads/2024/03/Wemos-data-folder-768x239.png 768w" sizes="auto, (max-width: 1040px) 100vw, 1040px" /></p>
<ol start="2">
<li>Legen Sie die Dateien, die Sie hochladen möchten, in das &#8218;data&#8216;-Verzeichnis</li>
<li>Wählen Sie in der Arduino IDE im Menü &#8218;Tools&#8216; den WeMos aus und wählen Sie eine Größe bei &#8218;Flash Size&#8216;</li>
<li>Das Dialogfeld für den seriellen Monitor schließen!</li>
<li>Wählen Sie aus dem Menü &#8218;Tools&#8216; die Option &#8218;ESP8266 Sketch Data Upload&#8216;.</li>
</ol>
<p><img loading="lazy" decoding="async" class="zoooom aligncenter wp-image-1529" src="https://nerd-corner.com/wp-content/uploads/2024/03/data-folder-upload.png" alt="Data folder upload" width="805" height="518" srcset="https://nerd-corner.com/wp-content/uploads/2024/03/data-folder-upload.png 851w, https://nerd-corner.com/wp-content/uploads/2024/03/data-folder-upload-300x193.png 300w, https://nerd-corner.com/wp-content/uploads/2024/03/data-folder-upload-768x495.png 768w" sizes="auto, (max-width: 805px) 100vw, 805px" /></p>
<p>Sobald der Upload abgeschlossen ist zeigt das Nachrichtenfenster der Arduino IDE 100% Upload an.</p>
<h2>WeMos Beispielprogramm zum Ein- und Ausschalten der OnBoard LED</h2>
<p>Ähnlich wie im ersten Teil wird der Webserver die OnBoard LED steuern. Als Basis dient ebenfalls der Code aus dem ersten Teil. Der überarbeitete Code sieht so aus:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="cpp" data-enlighter-theme="atomic" data-enlighter-title="D1_webserver_filesystem">#include &lt;ESP8266WiFi.h&gt;
#include &lt;ESP8266WebServer.h&gt;
#include &lt;ESP8266mDNS.h&gt;

ESP8266WebServer server(80);

void setup() {
  Serial.begin(115200); //Baudrate
  Serial.println("ESP starts");

  WiFi.begin("NerdCornerWiFi","NerdCornerPassword");


  Serial.print("Connecting...");

  while(WiFi.status()!=WL_CONNECTED){ //Loop which makes a point every 500ms until the connection process has finished

    delay(500);
    Serial.print(".");
  }
  Serial.println();

  Serial.print("Connected! IP-Address: ");
  Serial.println(WiFi.localIP()); //Displaying the IP Address

  if (MDNS.begin("nerd-corner")) {
    Serial.println("DNS started, available with: ");
    Serial.println("http://nerd-corner.local/");
  }

  server.serveStatic("/", SPIFFS, "/", "max-age=86400");
  SPIFFS.begin();

  server.onNotFound([](){ 
    server.send(404, "text/plain", "Landing page not found! Don't forget to name your landing page 'index.html'!");  
  });
 
  server.on("/led", HTTP_POST, []() {    
     
    const String ledState = server.arg("ledstate");
    if(ledState=="on"){
      switchLedOn();
    }
    else if(ledState=="off"){
      switchLedOff();
    }
    server.send(200, "text/json", "{\"result\":\"ok\"}");
  });

  server.begin();
  // initialize digital pin LED_BUILTIN as an output.
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  server.handleClient();
  MDNS.update();

}

void switchLedOff(){ 
  digitalWrite(LED_BUILTIN, HIGH);   // turn the D1 LED off 
}

void switchLedOn(){ 
  digitalWrite(LED_BUILTIN, LOW);    // turn the LED on 
}</pre>
<p>Auf ein paar Besonderheiten möchte ich dabei hinweisen. Wir haben beispielsweise folgendes hinzugefügt:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="cpp" data-enlighter-theme="atomic">server.serveStatic("/", SPIFFS, "/", "max-age=86400"); 
SPIFFS.begin();</pre>
<p>Ohne die beiden Zeilen wäre der Zugriff auf die Dateien im &#8222;data&#8220; Ordner nicht möglich. Bitte beachten, dass der Name &#8222;index.html&#8220; als default für die Landing Page eingestellt ist. Wenn man unbedingt möchte kann man das aber auch ändern.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="cpp" data-enlighter-theme="atomic">server.on("/led", HTTP_POST, []() {    
     
    const String ledState = server.arg("ledstate");
    if(ledState=="on"){
      switchLedOn();
    }
    else if(ledState=="off"){
      switchLedOff();
      }
      server.send(200, "text/json", "{\"result\":\"ok\"}");
  });</pre>
<p>Der &#8222;/led&#8220; Endpoint empfängt die Befehle vom Webserver. Lautet der Befehl &#8222;on&#8220; wird die LED eingeschaltet und bei &#8222;off&#8220; wird die LED ausgeschaltet.</p>
<h2>Wemos Webseite zum Ein- und Ausschalten der WeMos OnBoard LED</h2>
<p>Die Beispielwebseite ist ganz einfach aufgebaut. Sie besteht in erster Linie aus 2 Buttons zum Ein- und Ausschalten der LED.</p>
<p><img loading="lazy" decoding="async" class="zoooom aligncenter wp-image-1536" src="https://nerd-corner.com/wp-content/uploads/2024/03/Webseite-Wemos-Control.jpeg" alt="Website Wemos control LED" width="875" height="1946" srcset="https://nerd-corner.com/wp-content/uploads/2024/03/Webseite-Wemos-Control.jpeg 921w, https://nerd-corner.com/wp-content/uploads/2024/03/Webseite-Wemos-Control-135x300.jpeg 135w, https://nerd-corner.com/wp-content/uploads/2024/03/Webseite-Wemos-Control-461x1024.jpeg 461w, https://nerd-corner.com/wp-content/uploads/2024/03/Webseite-Wemos-Control-768x1708.jpeg 768w, https://nerd-corner.com/wp-content/uploads/2024/03/Webseite-Wemos-Control-691x1536.jpeg 691w" sizes="auto, (max-width: 875px) 100vw, 875px" /></p>
<p>Die Ordnerstruktur der Webseite ist sehr übersichtlich gehalten. Es gibt eine Hauptseite mit dem Namen &#8222;index.html&#8220; Dieser Name ist weltweit üblich für die Hauptseiten und wird auch automatisch vom WeMos entsprechend erkannt. Darüber hinaus einen &#8222;CSS&#8220; Ordner fürs Styling und einen &#8222;JS&#8220; Ordner für Funktionen.</p>
<p><img loading="lazy" decoding="async" class="zoooom aligncenter wp-image-1537" src="https://nerd-corner.com/wp-content/uploads/2024/03/Screenshot-data-file-system.png" alt="Screenshot esp8266 data file system" width="1045" height="347" srcset="https://nerd-corner.com/wp-content/uploads/2024/03/Screenshot-data-file-system.png 1152w, https://nerd-corner.com/wp-content/uploads/2024/03/Screenshot-data-file-system-300x100.png 300w, https://nerd-corner.com/wp-content/uploads/2024/03/Screenshot-data-file-system-1024x340.png 1024w, https://nerd-corner.com/wp-content/uploads/2024/03/Screenshot-data-file-system-768x255.png 768w" sizes="auto, (max-width: 1045px) 100vw, 1045px" /></p>
<p>Im Header Bereich der Webseite verlinken wir die Styles. Da wäre zum einen ein Standard Bootstrap, der alles automatisch bischen schöner macht und zusätzlich eine custom Styles Datei mit meinen eigenen Anpassungen. Außerdem werden im Header Bereich auch die Funktionen der Webseite verlinkt. Ich benutze den jQuery Standard um von der Webseite Requests an den WeMos zu senden. Meine eigenen custom Funktionen liegen in der &#8222;index.js&#8220;.</p>
<p>Bitte beachten, dass die jQuery Datei VOR der eigenen Datei eingebunden werden muss, sonst können keine jQuery Befehle im eigenen Code benutzt werden! Die eigenen Funktionen werden anschließend von den Buttons verwendet. Der HTML Code der Seite sieht insgesamt folgendermaßen aus:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="html" data-enlighter-theme="atomic">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;&lt;/html&gt;
&lt;html&gt;
  &lt;head&gt;
    &lt;meta charset="utf-8" /&gt;
    &lt;meta
      name="viewport"
      content="width=device-width, initial-scale=1, shrink-to-fit=no"
    /&gt;
    &lt;script type="text/javascript" src="js/jquery-3.5.1.min.js"&gt;&lt;/script&gt;
    &lt;script type="text/javascript" src="./js/index.js"&gt;&lt;/script&gt;
    &lt;link rel="stylesheet" href="css/bootstrap.min.css" /&gt;
    &lt;link rel="stylesheet" href="css/custom-style.css" /&gt;
    &lt;title&gt;D1 Webserver&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;h1&gt;D1 Webserver with filesystem&lt;/h1&gt;
    &lt;p&gt;
      This is an example for a WeMos Webserver with a filesystem. You can easily
      create webpages with html, css and js!
    &lt;/p&gt;

    &lt;h3&gt;Example to turn on and off the built in LED&lt;/h3&gt;
    &lt;button class="button-style" onclick="changeLEDState('on')"&gt;Turn on&lt;/button&gt;
    &lt;button class="button-style" onclick="changeLEDState('off')"&gt;
      Turn off
    &lt;/button&gt;

    &lt;h3&gt;Example to demo a JS function&lt;/h3&gt;
    &lt;button class="button-style" onclick="showAlert()"&gt;Show alert&lt;/button&gt;
  &lt;/body&gt;
&lt;/html&gt;
</pre>
<p>Besonderes Augenmerk gilt der JavaScript Funktion &#8222;changeLEDState(value)&#8220;</p>
<pre class="EnlighterJSRAW" data-enlighter-language="js" data-enlighter-theme="atomic">function changeLEDState(value) {
  $.post("/led", { ledstate: value });
}</pre>
<p>Dadurch, dass jQuery genutzt wird für die Kommunikation mit dem WeMos, reicht ein einfaches Dollarzeichen mit dem entsprechendem Request Befehl. Bei diesem POST Request wird ebenfalls ein Wert mit geschickt, welcher entweder &#8222;on&#8220; oder &#8222;off&#8220; ist zum ein- und ausschalten der LED.</p>
<p>Die Webseite kann nachfolgend als zip Datei heruntergeladen werden.</p>
<h2>Dateien zum Herunterladen</h2>
<ul>
<li><a  data-e-Disable-Page-Transition="true" class="download-link" title="" href="https://nerd-corner.com/de/download/1540/?tmstv=1755909777" rel="nofollow" id="download-link-1540" data-redirect="false" >
	Wemos example webserver to control OnBoard LED</a>
</li>
</ul>
<p>The post <a href="https://nerd-corner.com/de/wemos-d1-r2-ganze-webseite-mit-html-css-js-hosten/">WeMos D1 R2 &#8211; Ganze Webseite mit html, css &#038; js hosten</a> appeared first on <a href="https://nerd-corner.com/de">Nerd Corner</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://nerd-corner.com/de/wemos-d1-r2-ganze-webseite-mit-html-css-js-hosten/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>3D gedruckter Bit-Adapter Vierkant/Sechskant</title>
		<link>https://nerd-corner.com/de/3d-gedruckter-bit-adapter-vierkant-sechskant/</link>
					<comments>https://nerd-corner.com/de/3d-gedruckter-bit-adapter-vierkant-sechskant/#respond</comments>
		
		<dc:creator><![CDATA[Nerds]]></dc:creator>
		<pubDate>Thu, 05 Oct 2023 15:45:23 +0000</pubDate>
				<category><![CDATA[DIY]]></category>
		<category><![CDATA[Software-DE]]></category>
		<category><![CDATA[3D Druck]]></category>
		<category><![CDATA[Bit-Adapter]]></category>
		<category><![CDATA[Bits]]></category>
		<category><![CDATA[CAD]]></category>
		<category><![CDATA[CAD design]]></category>
		<category><![CDATA[Design]]></category>
		<category><![CDATA[Kreutzschlitz]]></category>
		<category><![CDATA[Sechskant]]></category>
		<category><![CDATA[Solid Works]]></category>
		<category><![CDATA[Steckschlüsselkasten]]></category>
		<category><![CDATA[Torx]]></category>
		<category><![CDATA[Vierkant]]></category>
		<category><![CDATA[Werkzeug]]></category>
		<guid isPermaLink="false">https://nerd-corner.com/?p=1486</guid>

					<description><![CDATA[<p>Ich kaufte einen Steckschlüsselkasten (1.0.1-1.0.3) 50zig bzw. 150zig teilig und dachte ich hätte für den Rest meines Lebens ausgesorgt. Aber leider geht im Laufe der &#8230; </p>
<p>The post <a href="https://nerd-corner.com/de/3d-gedruckter-bit-adapter-vierkant-sechskant/">3D gedruckter Bit-Adapter Vierkant/Sechskant</a> appeared first on <a href="https://nerd-corner.com/de">Nerd Corner</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Ich kaufte einen Steckschlüsselkasten (1.0.1-1.0.3) 50zig bzw. 150zig teilig und dachte ich hätte für den Rest meines Lebens ausgesorgt. Aber leider geht im Laufe der Zeit etwas verloren bzw. in die Brüche. Am häufigsten sind die Bits irgendwann ausgerissen bzw. abgerundet und man muss sie ersetzen. Ersatzwerkzeug vom Hersteller ist oft teuer oder nicht mehr vorrätig. Zusätzlich sind viele Werkzeuge im gleichen Kofferset nicht kompatibel zu einander. Der Grund sind fehlende Adapter, die eine Brücke zu anderen Werkzeugaufnahmen bilden können.</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1487 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/09/1_0.jpg" alt="Steckschlüsselkasten" width="2290" height="1373" srcset="https://nerd-corner.com/wp-content/uploads/2023/09/1_0.jpg 2292w, https://nerd-corner.com/wp-content/uploads/2023/09/1_0-300x180.jpg 300w, https://nerd-corner.com/wp-content/uploads/2023/09/1_0-1024x614.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2023/09/1_0-768x460.jpg 768w, https://nerd-corner.com/wp-content/uploads/2023/09/1_0-1536x921.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2023/09/1_0-2048x1228.jpg 2048w" sizes="auto, (max-width: 2290px) 100vw, 2290px" /></p>
<p>In meiner Werkstatt wird einiges geschraubt und dabei verwende ich am häufigsten Bits in den Varianten Torx, Innensechskant und Kreuzschlitz. Ich kaufe diese Bits immer in 10er oder 25er Packungen, weil die günstiger sind und ich gerne einen kleinen Vorrat habe. Beim Werkzeug gehts mir da ähnlich, deshalb habe ich beschlossen ein paar Bit-Adapter zu konstruieren und 3D zu drucken.</p>
<p><em><strong>Das könnte dich auch interessieren:</strong></em> <a href="https://nerd-corner.com/de/3d-gedruckter-ba…lter-fuer-cr2032/" target="_blank" rel="noopener">3D gedruckter Batteriebehälter</a></p>
<h2>Vorgehensweise</h2>
<p>Als erstes habe ich Ordnung in meinen Steckschlüsselkoffern gebracht um herauszufinden welche Adapter tatsächlich benötigt werden. Bei den gängigsten Vierkantgrößen für Steckschlüssel handelt es sich um:</p>
<ul>
<li>¼&#8220; gleich 6,35 mm</li>
<li>3/8&#8243; gleich 9,52 mm</li>
<li>½&#8220; gleich 12,7 mm</li>
</ul>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1491 zoooom" style="font-size: 1.125rem;" src="https://nerd-corner.com/wp-content/uploads/2023/09/2_0.jpg" alt="Bit-Adapter Vierkant" width="2290" height="1373" srcset="https://nerd-corner.com/wp-content/uploads/2023/09/2_0.jpg 2292w, https://nerd-corner.com/wp-content/uploads/2023/09/2_0-300x180.jpg 300w, https://nerd-corner.com/wp-content/uploads/2023/09/2_0-1024x614.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2023/09/2_0-768x460.jpg 768w, https://nerd-corner.com/wp-content/uploads/2023/09/2_0-1536x921.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2023/09/2_0-2048x1228.jpg 2048w" sizes="auto, (max-width: 2290px) 100vw, 2290px" /></p>
<p>Im Bild 2.0 habe ich einige Vierkantaufnahmen zusammengestellt. Bei Bild 2.0.1 handelt es sich um einen Sechskant Schaft ¼&#8220; mit drei unterschiedlichen Vierkantgrößen. Diesen Sechskant Schaft kann man gut in ein Dreibackenfutter einspannen welches üblicherweise bei einer Bohrmaschine oder Akkuschrauber genutzt wird. Im Bild 2.0.2 ist die Variante mit Handgriff dargestellt, bei dem am Schaftende der Vierkant sitzt. In 2.0.3 sieht man drei Gelenke mit unterschiedlichen Vierkantgrößen und Bild 2.0.4 zeigt ein sogenanntes „Federndes Druckstück“. Das federnde Druckstück hat die Aufgabe beim Einführen in das Gegenstück sich zurück zu ziehen und in der Endposition in die vorgesehene Kerbe oder Rille im Gegenstück einzurasten. Dies gewährleistet einen besseren Halt beider Teile in der Verbindung. Die Stelle des federnden Druckstücks am Vierkant, habe ich mit roten Kreisen an einigen Vierkanten exemplarisch markiert. Mir ist es wichtig die Funktion zu erklären, da in der Konstruktion des Adapters eine andere Lösung realisiert wird.</p>
<h2>CAD Konstruktion des Bit-Adapter<img loading="lazy" decoding="async" class="aligncenter wp-image-1490 zoooom" style="font-size: 1.125rem;" src="https://nerd-corner.com/wp-content/uploads/2023/09/3_0.jpg" alt="Bit-Adapter CAD" width="2290" height="1373" srcset="https://nerd-corner.com/wp-content/uploads/2023/09/3_0.jpg 2292w, https://nerd-corner.com/wp-content/uploads/2023/09/3_0-300x180.jpg 300w, https://nerd-corner.com/wp-content/uploads/2023/09/3_0-1024x614.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2023/09/3_0-768x460.jpg 768w, https://nerd-corner.com/wp-content/uploads/2023/09/3_0-1536x921.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2023/09/3_0-2048x1228.jpg 2048w" sizes="auto, (max-width: 2290px) 100vw, 2290px" /></h2>
<p>Die Realisierung am CAD geht eigentlich recht schnell von statten. Die Masse für die Tiefe des Vierkants auf der einen Seite und die Einstecktiefe des Sechskants auf der anderen Seite des Adapters wird an den Schäften gemessen. Bild 3.0.1 zeigt am Vierkant die Fläche (rot) die in den Adapter geschoben wird, welche gleich der Fläche im Bild 3.0.2 am Sechskant (schwarz) ist. Beim Bild 3.0.3 habe ich einen Schnitt gemacht und die Schnittfläche grün markiert. Die Platzierung der beiden Schäfte wird anhand der Farben dargestellt.</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1489 zoooom" style="font-size: 1.125rem;" src="https://nerd-corner.com/wp-content/uploads/2023/09/4_0.jpg" alt="CAD Bit-Adapter" width="2290" height="1373" srcset="https://nerd-corner.com/wp-content/uploads/2023/09/4_0.jpg 2292w, https://nerd-corner.com/wp-content/uploads/2023/09/4_0-300x180.jpg 300w, https://nerd-corner.com/wp-content/uploads/2023/09/4_0-1024x614.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2023/09/4_0-768x460.jpg 768w, https://nerd-corner.com/wp-content/uploads/2023/09/4_0-1536x921.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2023/09/4_0-2048x1228.jpg 2048w" sizes="auto, (max-width: 2290px) 100vw, 2290px" /></p>
<p>Für die Außenmaße der Adapter sind ausschließlich die Vierkant-Dimensionen ausschlaggebend. Da die Vierkantschäfte immer größer werden muss natürlich auch der Außendurchmesser des Adapters angepasst werden. Bei dem ½&#8220; Adapter (4.0.3) habe ich deshalb eine Stufe angebracht. Es ist schließlich beim Sechskant genügend Material vorhanden, da er sich nicht verändert. Die Aussparungen oder auch Taschen an den Mantelflächen sind keine Showeinlage. Sie dienen einerseits der Stabilität des Adapters und sparen Material. An allen Adaptern sind die beiden Gewinde gleich. Die M3 Gewinde dienen zum optionalen klemmen der Schäfte.</p>
<p>Ich verwende Stiftschrauben mit Innensechskant, die am Ende flach sind um die Oberfläche der Schäfte nicht zu beschädigen. Man kann auch normale Schrauben verwenden, wenn sie einen nicht stören. Den Sitz oder Position der Gewinde sieht man beim Bild 3.0.3 in der Schnittdarstellung.</p>
<p>Exkurs federnde Druckstücke: Die federnden Druckstücke wurden bereits in Bild 2.0.4 erwähnt. Schon seit einigen Jahren versuche ich sie in Kunststoff zu verwenden und musste immer wieder feststellen, dass es nicht funktioniert. Es ist leichter die Klemmung mit Schrauben zu gestalten und funktioniert  garantiert!</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1488 zoooom" style="font-size: 1.125rem;" src="https://nerd-corner.com/wp-content/uploads/2023/09/5_0.jpg" alt="Bit-Adapter" width="2290" height="1373" srcset="https://nerd-corner.com/wp-content/uploads/2023/09/5_0.jpg 2292w, https://nerd-corner.com/wp-content/uploads/2023/09/5_0-300x180.jpg 300w, https://nerd-corner.com/wp-content/uploads/2023/09/5_0-1024x614.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2023/09/5_0-768x460.jpg 768w, https://nerd-corner.com/wp-content/uploads/2023/09/5_0-1536x921.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2023/09/5_0-2048x1228.jpg 2048w" sizes="auto, (max-width: 2290px) 100vw, 2290px" /></p>
<p>Zum Schluss werden die Bit-Adapter gedruckt und gesäubert und die M3 Gewinde geschnitten. In den Bildern unter 5.0 sieht man an paar Beispiele für die Verwendung der Adapter.</p>
<h4>Anmerkung</h4>
<p>Die Bit-Adapter sind natürlich nur für den Einsatz unter 15Nm geeignet. Diesen Wert habe ich mit Karbonfaser verstärktem Filament locker erreicht. Bei höheren Werten besteht Bruchgefahr und daher auch Verletzungsgefahr!</p>
<h2>Dateien zum Herunterladen</h2>
<ul>
<li><a href="https://www.thingiverse.com/thing:5376154" target="_blank" rel="noopener">Adapter 3/8&#8243; zu  ¼&#8220;</a></li>
</ul>
<p>The post <a href="https://nerd-corner.com/de/3d-gedruckter-bit-adapter-vierkant-sechskant/">3D gedruckter Bit-Adapter Vierkant/Sechskant</a> appeared first on <a href="https://nerd-corner.com/de">Nerd Corner</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://nerd-corner.com/de/3d-gedruckter-bit-adapter-vierkant-sechskant/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>NLP Anwendung: Tensorflow.js vs Tensorflow Python &#8211; Teil 2</title>
		<link>https://nerd-corner.com/de/nlp-anwendung-tensorflow-js-vs-tensorflow-python-teil-2/</link>
					<comments>https://nerd-corner.com/de/nlp-anwendung-tensorflow-js-vs-tensorflow-python-teil-2/#comments</comments>
		
		<dc:creator><![CDATA[Nerds]]></dc:creator>
		<pubDate>Wed, 31 May 2023 17:15:48 +0000</pubDate>
				<category><![CDATA[Software-DE]]></category>
		<category><![CDATA[adam]]></category>
		<category><![CDATA[Decoder]]></category>
		<category><![CDATA[Encoder]]></category>
		<category><![CDATA[GPU]]></category>
		<category><![CDATA[hidden state]]></category>
		<category><![CDATA[JSON]]></category>
		<category><![CDATA[Keras]]></category>
		<category><![CDATA[Long short term memory]]></category>
		<category><![CDATA[loss function]]></category>
		<category><![CDATA[LSTM]]></category>
		<category><![CDATA[Map]]></category>
		<category><![CDATA[Natural Language Processing]]></category>
		<category><![CDATA[NLP]]></category>
		<category><![CDATA[OOV]]></category>
		<category><![CDATA[OOV Token]]></category>
		<category><![CDATA[optimizer]]></category>
		<category><![CDATA[Pad sequences]]></category>
		<category><![CDATA[Padding]]></category>
		<category><![CDATA[rmsprop]]></category>
		<category><![CDATA[Tensorflow]]></category>
		<category><![CDATA[Tensorflow Anleitung]]></category>
		<category><![CDATA[TensorFlow GPU]]></category>
		<category><![CDATA[Tensorflow python]]></category>
		<category><![CDATA[Tensorflow.js]]></category>
		<category><![CDATA[Tensorflow.js vs Tensorflow]]></category>
		<category><![CDATA[Tfjs]]></category>
		<category><![CDATA[Tokenisierung]]></category>
		<category><![CDATA[Tokenizer]]></category>
		<category><![CDATA[Txt Datensatz]]></category>
		<category><![CDATA[WordTokenizer]]></category>
		<guid isPermaLink="false">https://nerd-corner.com/de/?p=1426</guid>

					<description><![CDATA[<p>Im ersten Teil wurde das Einlesen und das Vorbereiten der Daten gezeigt. Außerdem wurde ausführlich die Tokenisierung des Datensatzes besprochen. Veranschaulicht wurden die Punkte anhand &#8230; </p>
<p>The post <a href="https://nerd-corner.com/de/nlp-anwendung-tensorflow-js-vs-tensorflow-python-teil-2/">NLP Anwendung: Tensorflow.js vs Tensorflow Python &#8211; Teil 2</a> appeared first on <a href="https://nerd-corner.com/de">Nerd Corner</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Im ersten Teil wurde das Einlesen und das Vorbereiten der Daten gezeigt. Außerdem wurde ausführlich die Tokenisierung des Datensatzes besprochen. Veranschaulicht wurden die Punkte anhand eines Beispiels in Tensorflow (Python) und Tensorflow.js (Tfjs). Sowohl bei dem Python Beispiel, als auch bei dem JavaScript Beispiel kann das Modell am Ende aber nur Wörter erkennen, die mindestens einmal im Datensatz vorgekommen sind. Mit neuen Wörtern hat dieses Modell Probleme, denn wir berücksichtigen hier keinen OOV Token.</p>
<p><em><strong>Das könnte dich auch interessieren:</strong> <a href="https://nerd-corner.com/de/nlp-anwendung-tensorflow-js-vs-tensorflow-python/">NLP Anwendung Teil 1 (Daten einlesen, Daten vorbereiten und Tokenisierung)</a></em></p>
<h2>OOV Token</h2>
<p>Bei einem Übersetzer ist es nur eine Frage der Zeit bis ein unbekanntes Wort eingegeben wird. Das kann ein Eigenname, ein Rechtschreibfehler oder ähnliches sein. Es empfiehlt sich daher das Modell auch im Hinblick auf unbekannte Wörter zu trainieren. Hierfür wird ein OOV Token benötigt. OOV steht für „out of vocabulary“. Während des Trainings lernt das Modell, diesen Token zu erzeugen oder entsprechend zu behandeln. In diesem Fall kann ein unbekanntes Wort durch den Token &#8222;&lt;oov&gt;&#8220; ersetzt werden, bevor es an das Modell übergeben wird. Das Modell wird es dann wie jedes andere Token behandeln und eine Antwort auf der Grundlage seines gelernten Verhaltens generieren.</p>
<p>Jetzt fragt man sich vielleicht wie inkludiert man diesen OOV Token in die Trainingsdaten? Ich mache das so, dass mein Datensatz zu Beginn automatisiert nach Wörtern durchsucht wird, die nur 1 Mal vorkommen. Diese seltenen Wörter ersetze ich dann durch „&lt;oov&gt;“, damit kann mein Modell lernen auch unbekannte Wörter zu reagieren.</p>
<h2>Padding</h2>
<p>Bei vielen Modellen des maschinellen Lernens, einschließlich neuronaler Netze, wird erwartet, dass die Eingaben eine feste Größe oder Form haben. Diese Anforderung ergibt sich aus der Struktur und dem Betrieb des zugrunde liegenden Berechnungsgraphen. Eingaben mit derselben Länge vereinfachen die Datenverarbeitungspipeline und ermöglichen eine effiziente Stapelverarbeitung.</p>
<h4>Warum die Eingaben gleich lang sein sollten:</h4>
<ol>
<li>Matrix-Operationen: Neuronale Netze verarbeiten Eingaben in der Regel in Stapeln, und die Stapelverarbeitung ist am effizientesten, wenn die Eingabedaten eine einheitliche Form haben. Die Daten sind in Matrizen organisiert, wobei jede Zeile eine Eingabeinstanz darstellt. Um Matrixoperationen effizient durchführen zu können, müssen alle Eingabeinstanzen die gleiche Form haben.</li>
<li>Gemeinsame Nutzung von Parametern: In vielen neuronalen Netzwerkarchitekturen werden die Modellparameter (Gewichte) auf verschiedene Teile der Eingabesequenz verteilt. In rekurrenten neuronalen Netzen (RNNs) werden beispielsweise dieselben Gewichte für die Verarbeitung jedes Zeitschritts verwendet. Um die gemeinsame Nutzung von Parametern zu ermöglichen, müssen alle Eingabesequenzen die gleiche Länge haben.</li>
<li>Speicherzuweisung: Neuronale Netze weisen den Speicher oft auf der Grundlage der maximalen Länge der Eingabesequenzen zu. Wenn die Sequenzen unterschiedliche Längen haben, ist eine dynamische Speicherzuweisung erforderlich, die komplexer und weniger effizient sein kann.</li>
</ol>
<p>Es ist zwar möglich, Eingaben mit variabler Länge durch Techniken wie Auffüllen und Maskieren zu verarbeiten, aber dies erhöht die Komplexität des Modells und kann zusätzliche Verarbeitungsschritte erfordern. Der Einfachheit und Effizienz halber ist es daher üblich, Sequenzen auf eine feste Länge aufzufüllen oder abzuschneiden, bevor sie in ein neuronales Netzmodell eingespeist werden.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="atomic">from keras.utils import pad_sequences

# pad sequences
encoder_seq = pad_sequences(encoder, maxlen=max_encoder_sequence_len, padding="post")
decoder_inp = pad_sequences([arr[:-1] for arr in decoder], maxlen=max_decoder_sequence_len, padding="post")
decoder_output = pad_sequences([arr[1:] for arr in decoder], maxlen=max_decoder_sequence_len, padding="post")
print(encoder_seq)
print([idx_2_txt_encoder[i] for i in encoder_seq[0]])
print([idx_2_txt_decoder[i] for i in decoder_inp[0]])
print([idx_2_txt_decoder[i] for i in decoder_output[0]])</pre>
<p>Zur besseren Veranschaulichung habe ich die 4 print Befehle hinzugefügt. Anfangs dachte ich, dass der längste Satz im Datensatz die Länge für Input Daten und Output Daten vorgibt. Also die Paddinglänge für Input und Output gleich wäre. Das ist aber nicht der Fall! Input Daten und Output Daten sind auf unterschiedliche Längen normiert!</p>
<p>In dem Beispiel hier, habe ich einen winzigen Datensatz benutzt bei dem der längste englische Satz aus 3 Wörtern besteht und der längste französische Satz aus 10 Wörtern. Demnach wird mit &#8222;&lt;pad&gt;&#8220; bzw 0 jeder Trainingssatz aufgefüllt bis der Input 3 bzw. der Output 10 Wörter erreicht hat.</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1398 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/05/sampleOutputPadding.png" alt="Sample output padding tensorflow" width="1230" height="351" srcset="https://nerd-corner.com/wp-content/uploads/2023/05/sampleOutputPadding.png 1243w, https://nerd-corner.com/wp-content/uploads/2023/05/sampleOutputPadding-300x86.png 300w, https://nerd-corner.com/wp-content/uploads/2023/05/sampleOutputPadding-1024x292.png 1024w, https://nerd-corner.com/wp-content/uploads/2023/05/sampleOutputPadding-768x219.png 768w" sizes="auto, (max-width: 1230px) 100vw, 1230px" /></p>
<p>Der Decoder Output mit <strong>[arr[1:] for arr in decoder] </strong>entfernt den &#8222;start&#8220; token und der Decoder Input mit <strong>[arr[:-1] for arr in decoder] </strong>entfernt den &#8222;end&#8220; token.</p>
<p>Bei Sequenz-zu-Sequenz-Modellen wird der Decoder darauf trainiert, die Ausgabesequenz auf der Grundlage der Eingabesequenz und der zuvor generierten Token zu erzeugen. Während des Trainings enthält die Eingangssequenz des Decoders den &#8222;Start&#8220;-Token, der als Initialisierungs-Token für den Decoder dient. Beim Training des Decoders soll dieser jedoch den nächsten Token auf der Grundlage der zuvor generierten Token vorhersagen, mit Ausnahme des &#8222;Start&#8220;-Tokens. Daher wird bei der Vorbereitung der Decoder-Ausgabesequenz das &#8222;Start&#8220;-Token aus jeder Sequenz entfernt. Dies geschieht, um die Eingangs- und Ausgangssequenzen des Decoders korrekt aufeinander abzustimmen. Die Decoder-Eingangssequenz enthält den &#8222;Start&#8220;-Token und schließt den &#8222;End&#8220;-Token aus, während die Decoder-Ausgangssequenz den &#8222;End&#8220;-Token enthält und den &#8222;Start&#8220;-Token ausschließt. Auf diese Weise stellen wir sicher, dass der Decoder lernt, die richtige Ausgabesequenz auf der Grundlage der Eingabe zu erzeugen.</p>
<p>Während der Inferenz (Modellanwendung nach dem Training) bzw. der Übersetzung können wir bei der Verwendung des trainierten Modells zur Erzeugung von Übersetzungen mit dem &#8222;Start&#8220;-Token beginnen und iterativ Token erzeugen, bis wir auf das &#8222;End&#8220;-Token stoßen oder eine maximale Sequenzlänge erreichen.</p>
<p>Beim Padding für Tensorflow.js übernehmen wir 1:1 die Vorgehensweise von Python. Leider haben wir auch hier wieder mehr Arbeit und mehr Code Zeilen, da in Tfjs keine padSequences Funktion existiert. Ich habe mir deswegen eine eigene padSequences Funktion geschrieben:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="js" data-enlighter-theme="atomic">function padSequences(sequences) {
  const paddedSequences = [];
  const maxlen = findMaxLength(sequences);

  for (const sequence of sequences) {
    if (sequence.length &gt;= maxlen) {
      paddedSequences.push(sequence.slice(0, maxlen));
    } else {
      const paddingLength = maxlen - sequence.length;
      const paddingArray = new Array(paddingLength).fill(0);
      const paddedSequence = sequence.concat(paddingArray);
      paddedSequences.push(paddedSequence);
    }
  }

  return paddedSequences;
}</pre>
<p>Anschließend können wir mit Hilfe dieser Funktion unseren encoder, decoder Input und decoder Output bestimmen:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="js" data-enlighter-theme="atomic">function pad(data) {
  const encoderSeq = padSequences(data.en);
  const decoderInp = padSequences(data.de.map((arr) =&gt; arr.slice(0, -1))); // Has startToken
  const decoderOutput = padSequences(data.de.map((arr) =&gt; arr.slice(1))); // Has endToken
  console.log(decoderInp);
}</pre>
<p>Bei mir ist die „1“ der „startToken“ daher sieht der decoder Input beispielsweise so aus:</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1420 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/06/decoder-Input-example-JS.png" alt="Decoder Input Example JS" width="490" height="376" srcset="https://nerd-corner.com/wp-content/uploads/2023/06/decoder-Input-example-JS.png 499w, https://nerd-corner.com/wp-content/uploads/2023/06/decoder-Input-example-JS-300x230.png 300w" sizes="auto, (max-width: 490px) 100vw, 490px" /></p>
<h2>Modell erstellen</h2>
<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="atomic"># Design LSTM NN (Encoder &amp; Decoder)
# encoder model
encoder_input = Input(shape=(None,), name="encoder_input_layer")
encoder_embedding = Embedding(num_encoder_tokens, 300, input_length=max_encoder_sequence_len, name="encoder_embedding_layer")(encoder_input)
encoder_lstm = LSTM(256, activation="tanh", return_sequences=True, return_state=True, name="encoder_lstm_1_layer")(encoder_embedding)
encoder_lstm2 = LSTM(256, activation="tanh", return_state=True, name="encoder_lstm_2_layer")(encoder_lstm)
_, state_h, state_c = encoder_lstm2
encoder_states = [state_h, state_c]

# decoder model
decoder_input = Input(shape=(None,), name="decoder_input_layer")
decoder_embedding = Embedding(num_decoder_tokens, 300, input_length=max_decoder_sequence_len, name="decoder_embedding_layer")(decoder_input)
decoder_lstm = LSTM(256, activation="tanh", return_state=True, return_sequences=True, name="decoder_lstm_layer")
decoder_outputs, _, _ = decoder_lstm(decoder_embedding, initial_state=encoder_states)
decoder_dense = Dense(num_decoder_tokens+1, activation="softmax", name="decoder_final_layer")
outputs = decoder_dense(decoder_outputs)

model = Model([encoder_input, decoder_input], outputs)</pre>
<p>Das Codebeispiel zeigt den  Entwurf eines neuronalen Netzes mit Long Short-Term Memory (LSTM) für Sequenz-zu-Sequenz-Lernen (Seq2Seq) dar, das typischerweise für Aufgaben wie maschinelle Übersetzung verwendet wird. Der Code definiert zwei Hauptteile:  Encoder-Modell und Decoder-Modell.</p>
<h3>Encoder-Modell:</h3>
<ul>
<li style="list-style-type: none;">
<ul>
<li>Die Encoder-Eingangsschicht (<strong>encoder_input</strong>) stellt die Eingangssequenz des Encoder-Modells dar.</li>
<li>Die Eingangssequenz wird mithilfe einer Einbettungsschicht (<strong>encoder_embedding</strong>) eingebettet, die jedes Token in eine dichte Vektordarstellung umwandelt.</li>
<li>Die eingebettete Sequenz wird dann durch die erste LSTM-Schicht (<strong>encoder_lstm_1_layer</strong>) geleitet, um sequenzielle Informationen zu erfassen. Die LSTM-Schicht gibt die Ausgabesequenz und den endgültigen versteckten Zustand zurück.</li>
<li>Die Ausgabesequenz der ersten LSTM-Schicht wird von der zweiten LSTM-Schicht (<strong>encoder_lstm_2_layer</strong>) weiterverarbeitet. Die zweite LSTM-Schicht liefert nur den endgültigen versteckten Zustand, der die zusammengefasste Information der Eingabesequenz darstellt.</li>
<li>Der final hidden Zustand der zweiten LSTM-Schicht wird in den final hidden Zustand (<strong>state_h</strong>) und den final Zellzustand (<strong>state_c</strong>) aufgeteilt, die als Anfangszustände für das Decodermodell verwendet werden.</li>
<li>Die Zustände des Encoder-Modells sind als <strong>encoder_states</strong> definiert und werden an das Decoder-Modell weitergegeben.</li>
</ul>
</li>
</ul>
<h3>Decoder-Modell:</h3>
<ul>
<li>Die Decoder-Eingangsschicht (<strong>decoder_input</strong>) stellt die Eingangssequenz des Decoder-Modells dar, die aus der um eine Position verschobenen Zielsequenz besteht.</li>
<li>Ähnlich wie beim Encoder wird die Eingangssequenz mit Hilfe einer Einbettungsschicht (<strong>decoder_embedding</strong>) eingebettet.</li>
<li>Die eingebettete Sequenz wird dann durch eine LSTM-Schicht (<strong>decoder_lstm_layer</strong>) geleitet, wobei die Anfangszustände auf die Endzustände des Encoder-Modells gesetzt werden. Dies ermöglicht es dem Decoder, die relevanten Informationen des Encoders zu berücksichtigen.</li>
<li>Die LSTM-Schicht liefert die Ausgabesequenz und die Endzustände.</li>
<li>Die Ausgabesequenz aus der LSTM-Schicht wird durch eine dichte Schicht (<strong>decoder_final_layer</strong>) mit einer Softmax-Aktivierungsfunktion geleitet, die die Wahrscheinlichkeitsverteilung über die ausgegebenen Token vorhersagt.</li>
</ul>
<p>Die Klasse <strong>Model</strong> wird zur Erstellung des Gesamtmodells verwendet, indem die Eingabeschichten (<strong>[encoder_input, decoder_input]</strong>) und die Ausgabeschicht (<strong>outputs</strong>) angegeben werden. Diese Modellarchitektur folgt der Grundstruktur eines Encoder-Decoder-Modells unter Verwendung von LSTMs, bei dem der Encoder die Eingabesequenz verarbeitet und den Kontextvektor (final hidden state) erzeugt, der dann vom Decoder zur Erzeugung der Ausgabesequenz verwendet wird.</p>
<p>Das selbe Modell lässt sich auch in JS umsetzen:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="js" data-enlighter-theme="atomic">function createModell(
  numEncoderTokens,
  numDecoderTokens,
  maxEncoderSequenceLen,
  maxDecoderSequenceLen
) {
  // Encoder model
  const encoderInput = tf.input({ shape: [null], name: "encoderInputLayer" });
  const encoderEmbedding = tf.layers
    .embedding({
      inputDim: numEncoderTokens,
      outputDim: 300,
      inputLength: maxEncoderSequenceLen,
      name: "encoderEmbeddingLayer",
    })
    .apply(encoderInput);
  const encoderLstm = tf.layers
    .lstm({
      units: 256,
      activation: "tanh",
      returnSequences: true,
      returnState: true,
      name: "encoderLstm1Layer",
    })
    .apply(encoderEmbedding);
  const [_, state_h, state_c] = tf.layers
    .lstm({
      units: 256,
      activation: "tanh",
      returnState: true,
      name: "encoderLstm2Layer",
    })
    .apply(encoderLstm);
  const encoderStates = [state_h, state_c];

  // Decoder model
  const decoderInput = tf.input({ shape: [null], name: "decoderInputLayer" });
  const decoderEmbedding = tf.layers
    .embedding({
      inputDim: numDecoderTokens,
      outputDim: 300,
      inputLength: maxDecoderSequenceLen,
      name: "decoderEmbeddingLayer",
    })
    .apply(decoderInput);
  const decoderLstm = tf.layers.lstm({
    units: 256,
    activation: "tanh",
    returnState: true,
    returnSequences: true,
    name: "decoderLstmLayer",
  });
  const [decoderOutputs, ,] = decoderLstm.apply(decoderEmbedding, {
    initialState: encoderStates,
  });
  const decoderDense = tf.layers.dense({
    units: numDecoderTokens + 1,
    activation: "softmax",
    name: "decoderFinalLayer",
  });
  const outputs = decoderDense.apply(decoderOutputs);

  const model = tf.model({ inputs: [encoderInput, decoderInput], outputs });
  return model;
}</pre>
<h2>Modell trainieren und speichern</h2>
<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="atomic"># train model
loss = tf.losses.SparseCategoricalCrossentropy()
model.compile(optimizer='rmsprop', loss=loss, metrics=['accuracy'])
callback = tf.keras.callbacks.EarlyStopping(monitor='loss', patience=3)
history = model.fit(
   [encoder_seq, decoder_inp],
   decoder_output,
   epochs=80,  # 80
   batch_size=450,  # 450
   # callbacks=[callback]
)</pre>
<p>Die Funktion model.fit() wird zum Trainieren des Modells verwendet. Die Trainingsdaten bestehen aus den Encoder-Eingangssequenzen (<strong>encoder_seq</strong>), den Decoder-Eingangssequenzen (<strong>decoder_inp</strong>) und den Decoder-Ausgangssequenzen (<strong>decoder_output</strong>). Das Training wird für eine bestimmte Anzahl von Epochen (epochs) und eine Batchgröße von 450 durchgeführt. Der Trainingsfortschritt kann mit dem EarlyStopping-Callback überwacht werden, der das Training abbricht, wenn sich der Verlust nach einer bestimmten Anzahl von Epochen nicht verbessert hat. Der Trainingsverlauf wird in der Variable history gespeichert.</p>
<p>Das Modell in Tensorflow kann sowohl Tensoren als auch Numpy-Arrays als Eingaben verarbeiten. Wenn man Numpy-Arrays als Eingaben an die Funktion <strong>fit</strong> in TensorFlow übergibt, konvertiert diese sie intern automatisch in Tensoren, bevor das Training durchgeführt wird. Im Code werden die <strong>encoder_seq</strong>, <strong>decoder_inp</strong> und <strong>decoder_output</strong> Arrays automatisch in Tensoren umgewandelt, wenn man sie an die <strong>fit</strong> Funktion übergibt. Dies erlaubt es TensorFlow, die notwendigen Berechnungen während des Trainingsprozesses durchzuführen.</p>
<p>In ähnlicher Weise kann die Funktion <strong>fit</strong> in TensorFlow.js sowohl mit Tensoren als auch mit Arrays umgehen. Man kann also direkt sein 2D-Array (<strong>encoderSeq</strong>) als erste Eingabe übergeben und TensorFlow.js wird sie intern in Tensoren für das Training umwandeln. Obwohl man Arrays anstelle von Tensoren übergibt, sind TensorFlow und TensorFlow.js in der Lage, die Konvertierung intern zu handhaben und das Training entsprechend durchzuführen.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="atomic"># save model
model.save("./model-experimental/Translate_Eng_FR.h5")
model.save_weights("./model-experimental/model_NMT")</pre>
<p>Es ist üblich, die Gewichte eines trainierten Modells getrennt von der Modellarchitektur zu speichern. Die separate Speicherung der Gewichte und der Architektur ermöglicht mehr Flexibilität beim Laden und Verwenden des Modells. So kann man beispielsweise nur die Gewichte laden, wenn die Modellarchitektur an anderer Stelle definiert wurde oder wenn die Gewichte in einem anderen Modell mit einer ähnlichen Architektur verwenden werden sollen.</p>
<p>Abschließend auch der Code in JavaScript:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="js" data-enlighter-theme="atomic">async function trainModel(data) {
  const encoderSeq = padSequences(data.en);
  const decoderInp = padSequences(data.de.map((arr) =&gt; arr.slice(0, -1))); // Has startToken
  const decoderOutput = padSequences(data.de.map((arr) =&gt; arr.slice(1))); // Has endToken

  data.model.compile({
    optimizer: "rmsprop",
    loss: "sparseCategoricalCrossentropy",
    metrics: ["accuracy"],
  });
  const history = await data.model.fit(
    [encoderSeq, decoderInp],
    decoderOutput,
    {
      epochs: 80,
      batch_size: 450,
    }
  );
}</pre>
<p>An dieser Stelle kommt meine Frustration mit Tensorflow.js ins Spiel. Obwohl jeder Schritt 1:1 dem Schritt in Python entspricht klappt das Training des Modells in Tensorflow.js nicht&#8230; Ich erhalte immer eine Fehlermeldung:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="atomic">C:\Users\[...]\node_modules\@tensorflow\tfjs-layers\dist\tf-layers.node.js:23386
            if (array.shape.length !== shapes[i].length) {
                            ^

TypeError: Cannot read properties of undefined (reading 'length')
    at standardizeInputData</pre>
<h2>Allgemein Verlustfunktion und Optimierer</h2>
<p>Verlustfunktionen und Optimierer sind Schlüsselkomponenten beim Training eines maschinellen Lernmodells. Eine Verlustfunktion, die auch als Ziel- oder Kostenfunktion bezeichnet wird, misst die Leistung eines Modells, indem sie die Unähnlichkeit zwischen den vorhergesagten Ausgaben und den tatsächlichen Zielen quantifiziert. Das Ziel des Trainings eines Modells ist es, diese Verlustfunktion zu minimieren, was im Wesentlichen bedeutet, dass die Fähigkeit des Modells, genaue Vorhersagen zu treffen, verbessert wird. Die Wahl der Verlustfunktion hängt von der jeweiligen Problemstellung ab. Bei Klassifizierungsaufgaben sind beispielsweise die kategoriale Kreuzentropie, die binäre Kreuzentropie und die Softmax-Kreuzentropie gängige Verlustfunktionen, während bei Regressionsaufgaben häufig der mittlere quadratische Fehler (<a href="https://de.wikipedia.org/wiki/Mittlere_quadratische_Abweichung" target="_blank" rel="noopener">MSE</a>) und der mittlere absolute Fehler (MAE) verwendet werden.</p>
<p>Ein Optimierer hingegen ist für die Aktualisierung der Modellparameter (Gewichte und Verzerrungen) während des Trainings verantwortlich, um die Verlustfunktion zu minimieren. Er bestimmt, wie die Parameter des Modells auf der Grundlage der berechneten Gradienten der Verlustfunktion in Bezug auf diese Parameter angepasst werden. Optimierer verwenden verschiedene Algorithmen und Techniken, um effizient nach den optimalen Werten der Parameter zu suchen. Zu den gängigen Optimierern gehören Stochastic Gradient Descent (SGD), Adam, RMSprop und Adagrad. Jeder Optimierer hat seine eigenen Merkmale und Hyperparameter, die den Trainingsprozess und die Konvergenzgeschwindigkeit des Modells beeinflussen können.</p>
<p>Die Wahl der Verlustfunktion und des Optimierers hängt von der spezifischen Aufgabe, der Modellarchitektur und den Eigenschaften des Datensatzes ab. Es ist wichtig, geeignete Verlustfunktionen und Optimierer auszuwählen, um ein effektives Modelltraining und eine Konvergenz zur optimalen Leistung zu gewährleisten.</p>
<h2>Häufig verwendete Verlustfunktionen und Optimierer</h2>
<h4>Verlustfunktionen:</h4>
<ul>
<li>Kategoriale Kreuz-Entropie: Diese Verlustfunktion wird häufig in Sequenz-zu-Sequenz-Modellen für Mehrklassen-Klassifizierungsprobleme verwendet, bei denen jedes Zielwort als eine eigene Klasse behandelt wird.</li>
<li> Spärliche kategoriale Kreuzentropie: Ähnlich wie die kategoriale Kreuzentropie, aber geeignet, wenn die Zielsequenzen als spärliche ganzzahlige Sequenzen dargestellt werden (z. B. unter Verwendung von Wortindizes).</li>
</ul>
<h4>Optimierer:</h4>
<ul>
<li>Adam: Adam ist ein beliebter Optimierer, der die Vorteile des Adaptiven Gradientenalgorithmus (AdaGrad) und der Root Mean Square Propagation (RMSprop) kombiniert. Er passt die Lernrate für jeden Parameter auf der Grundlage früherer Gradienten an, was zu einer schnelleren Konvergenz und einer besseren Handhabung spärlicher Gradienten beiträgt.</li>
<li>RMSprop: RMSprop ist ein Optimierer, der einen gleitenden Durchschnitt der quadrierten Gradienten für jeden Parameter beibehält. Er passt die Lernrate auf der Grundlage der Größe des Gradienten an, was eine schnellere Konvergenz und eine bessere Leistung bei nicht stationären Zielen ermöglicht.</li>
<li>Adagrad: Adagrad passt die Lernrate individuell für jeden Parameter an, basierend auf der historischen Gradientenakkumulation. Es führt größere Updates für seltene Parameter und kleinere Updates für häufige Parameter durch.</li>
</ul>
<h2>Dateien zum Herunterladen</h2>
<ul>
<li><a  data-e-Disable-Page-Transition="true" class="download-link" title="" href="https://nerd-corner.com/de/download/1432/?tmstv=1755909777" rel="nofollow" id="download-link-1432" data-redirect="false" >
	NLP Tensorflow.js code (model has an error!)</a>
</li>
</ul>
<p>The post <a href="https://nerd-corner.com/de/nlp-anwendung-tensorflow-js-vs-tensorflow-python-teil-2/">NLP Anwendung: Tensorflow.js vs Tensorflow Python &#8211; Teil 2</a> appeared first on <a href="https://nerd-corner.com/de">Nerd Corner</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://nerd-corner.com/de/nlp-anwendung-tensorflow-js-vs-tensorflow-python-teil-2/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>NLP Anwendung: Tensorflow.js vs Tensorflow Python &#8211; Teil 1</title>
		<link>https://nerd-corner.com/de/nlp-anwendung-tensorflow-js-vs-tensorflow-python-teil-1/</link>
					<comments>https://nerd-corner.com/de/nlp-anwendung-tensorflow-js-vs-tensorflow-python-teil-1/#respond</comments>
		
		<dc:creator><![CDATA[Nerds]]></dc:creator>
		<pubDate>Wed, 31 May 2023 17:01:38 +0000</pubDate>
				<category><![CDATA[Software-DE]]></category>
		<category><![CDATA[CSV Datensatz]]></category>
		<category><![CDATA[CSV einlesen]]></category>
		<category><![CDATA[Decoder]]></category>
		<category><![CDATA[Encoder]]></category>
		<category><![CDATA[GPU]]></category>
		<category><![CDATA[hidden state]]></category>
		<category><![CDATA[JSON]]></category>
		<category><![CDATA[Keras]]></category>
		<category><![CDATA[Long short term memory]]></category>
		<category><![CDATA[loss function]]></category>
		<category><![CDATA[LSTM]]></category>
		<category><![CDATA[Map]]></category>
		<category><![CDATA[natural Bibliothek]]></category>
		<category><![CDATA[Natural Language Processing]]></category>
		<category><![CDATA[NLP]]></category>
		<category><![CDATA[NLP Anwendung]]></category>
		<category><![CDATA[OOV]]></category>
		<category><![CDATA[OOV Token]]></category>
		<category><![CDATA[optimizer]]></category>
		<category><![CDATA[Pad sequences]]></category>
		<category><![CDATA[Padding]]></category>
		<category><![CDATA[rmsprop]]></category>
		<category><![CDATA[Tensorflow]]></category>
		<category><![CDATA[Tensorflow Anleitung]]></category>
		<category><![CDATA[TensorFlow GPU]]></category>
		<category><![CDATA[Tensorflow python]]></category>
		<category><![CDATA[Tensorflow.js]]></category>
		<category><![CDATA[Tensorflow.js vs Tensorflow]]></category>
		<category><![CDATA[Tfjs]]></category>
		<category><![CDATA[Tokenisierung]]></category>
		<category><![CDATA[Txt Datensatz]]></category>
		<category><![CDATA[WordTokenizer]]></category>
		<guid isPermaLink="false">https://nerd-corner.com/?p=1387</guid>

					<description><![CDATA[<p>Ich arbeite aktuell an einem Projekt bei dem ich einen Deutsch zu Bairisch Übersetzer mittels Machine Learning programmieren will. Man bezeichnet das als Natural Language &#8230; </p>
<p>The post <a href="https://nerd-corner.com/de/nlp-anwendung-tensorflow-js-vs-tensorflow-python-teil-1/">NLP Anwendung: Tensorflow.js vs Tensorflow Python &#8211; Teil 1</a> appeared first on <a href="https://nerd-corner.com/de">Nerd Corner</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Ich arbeite aktuell an einem Projekt bei dem ich einen Deutsch zu Bairisch Übersetzer mittels Machine Learning programmieren will. Man bezeichnet das als Natural Language Processing (NLP). Häufig wird für die Umsetzung eine Google Library namens Tensorflow benutzt. Es existiert sowohl Tensorflow.js als auch Tensorflow (Python). Da ich beruflich mit Angular entwickle und daher mit TypeScript bzw. JavaScript vertraut bin, habe ich mich anfangs direkt für die NLP Anwendung in Tensorflow.js entschieden. Ich war so naiv anzunehmen, dass der einzige Unterschied zwischen den beiden Libraries die verwendete Programmiersprache wäre. Das ist definitiv nicht der Fall! Für mein NLP Projekt fehlen teilweise grundlegende Funktionen in Tensorflow.js (wie beispielsweise ein Tokenizer). <a href="https://nerd-corner.com/de/tensorflow-js-oder-tensorflow-nutzen/">In diesem Beitrag habe ich die allgemeinen Unterschiede zwischen Tensorflow.js und Tensorflow (Python) erklärt.</a></p>
<p>Ich habe viele Abende damit verbracht mein Projekt mit Tensorflow.js zum Laufen zu bringen und bin am Ende gescheitert. Der Wechsel auf Python brachte dann den erhofften Durchbruch! Ich würde jedem empfehlen für NLP Anwendungen Python zu nutzen! Nichtsdestotrotz möchte ich in diesem Beitrag einmal die Unterschiede zwischen Tensorflow.js und Tensorflow im Bezug auf mein Projekt anhand von Codebeispielen verdeutlichen. Zwischendurch werde ich auch so gut wie möglich mein neu angesammeltes Wissen in die jeweiligen Abschnitte einbauen.</p>
<p><em><strong>Das könnte dich auch interessieren:</strong> <a href="https://nerd-corner.com/de/nlp-anwendung-tensorflow-js-vs-tensorflow-python-teil-2/">NLP Anwendung Teil 2 (OOV Token, Padding, Modell erstellen und Modell trainieren)</a></em></p>
<h2>Daten einlesen</h2>
<p>Zunächst einmal braucht man einen Datensatz mit dem später das Modell trainiert werden soll. Hier kann ich <a href="https://www.kaggle.com/">https://www.kaggle.com/</a> empfehlen. Man findet dort eine Vielzahl an Datensätzen zur freien Verfügung und teilweise sogar weiterführende Codebeispiele. Den Datensatz kann man entweder per Link einlesen oder downloaden und dann lokal vom File System einlesen. Ein guter Datensatz sollte über 100.000 Beispiele enthalten. Am besten auch teilweise ganze Paragraphen. So sieht beispielsweise ein Englisch/Französischer Datensatz als CSV aus:</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1389 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/05/sample-dataset-tensorflow.png" alt="Sample dataset tensorflow CSV" width="1380" height="332" srcset="https://nerd-corner.com/wp-content/uploads/2023/05/sample-dataset-tensorflow.png 1393w, https://nerd-corner.com/wp-content/uploads/2023/05/sample-dataset-tensorflow-300x72.png 300w, https://nerd-corner.com/wp-content/uploads/2023/05/sample-dataset-tensorflow-1024x246.png 1024w, https://nerd-corner.com/wp-content/uploads/2023/05/sample-dataset-tensorflow-768x185.png 768w" sizes="auto, (max-width: 1380px) 100vw, 1380px" /></p>
<p>Zunächst die simple Variante mittels Python:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-title="Read in CSV" data-enlighter-theme="atomic">import pandas as pd

# read in dataSet for training
df = pd.read_csv("./dataset/eng_-french.csv")
df.columns = ["english", "french"]
print(df.head())
print(df.info())</pre>
<p>Wir nutzen die pandas Library und lesen damit die CSV ein. Mit dem head() können wir testen ob es funktioniert hat und uns die ersten 5 Zeilen anzeigen lassen. Mittels info() erhalten wir weitere Infos wie beispielsweise Anzahl der Spalten und Anzahl der Reihen:</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1388 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/05/output-read-in-CSV-python.png" alt="output CSV read in with python pandas lib" width="575" height="516" srcset="https://nerd-corner.com/wp-content/uploads/2023/05/output-read-in-CSV-python.png 583w, https://nerd-corner.com/wp-content/uploads/2023/05/output-read-in-CSV-python-300x269.png 300w" sizes="auto, (max-width: 575px) 100vw, 575px" /></p>
<p>Zum Vergleich in Tensorflow.js (Tfjs) gibt es auch eine Möglichkeit CSV einzulesen:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="js" data-enlighter-title="index.js" data-enlighter-theme="atomic">const tf = require("@tensorflow/tfjs");

async function readInData() {
  await tf.ready();
  const languageDataSet = tf.data.csv("file://" + "./ger_en_trans.csv");

  // Extract language pairs
  const dataset = languageDataSet.map((record) =&gt; ({
    en: record.en,
    de: record.de,
  }));

  const pairs = await dataset.toArray();

  console.log(pairs);
}

readInData();</pre>
<p>Ich habe dabei zunächst versucht den gleichen Datensatz wie in der Python Version einzulesen:</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1408 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/05/Tensorflow.js-ReadIn-Same-CSV.png" alt="read in same CSV as in tf" width="745" height="220" srcset="https://nerd-corner.com/wp-content/uploads/2023/05/Tensorflow.js-ReadIn-Same-CSV.png 758w, https://nerd-corner.com/wp-content/uploads/2023/05/Tensorflow.js-ReadIn-Same-CSV-300x89.png 300w" sizes="auto, (max-width: 745px) 100vw, 745px" /></p>
<p>Anschließend wollte ich die Überschriften kürzen in der originalen CSV, dadurch bekam ich aber seltsamer Weise eine Fehlermeldung beim Einlesen. Selbst als ich den Originalzustand der CSV wiederhergestellt hatte, blieb der Fehler:</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1407 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/05/read-in-modified-CSV-before-it-breaks-tfjs.png" alt="error when reading in CSV" width="610" height="298" srcset="https://nerd-corner.com/wp-content/uploads/2023/05/read-in-modified-CSV-before-it-breaks-tfjs.png 620w, https://nerd-corner.com/wp-content/uploads/2023/05/read-in-modified-CSV-before-it-breaks-tfjs-300x147.png 300w" sizes="auto, (max-width: 610px) 100vw, 610px" /></p>
<p>Letztlich habe ich mich dann dafür entschieden einen anderen Datensatz zu nutzen:</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1406 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/05/CSV-sample-screenshot-en-de.png" alt="other CSV data samples" width="375" height="356" srcset="https://nerd-corner.com/wp-content/uploads/2023/05/CSV-sample-screenshot-en-de.png 389w, https://nerd-corner.com/wp-content/uploads/2023/05/CSV-sample-screenshot-en-de-300x285.png 300w" sizes="auto, (max-width: 375px) 100vw, 375px" /></p>
<p>Der war beim Einlesen auch wesentlich besser lesbar:</p>
<p><img loading="lazy" decoding="async" class="zoooom aligncenter wp-image-1405" src="https://nerd-corner.com/wp-content/uploads/2023/05/reading-in-the-german-english-CSV.png" alt="NLP Anwendung Tensorflow.js" width="825" height="276" srcset="https://nerd-corner.com/wp-content/uploads/2023/05/reading-in-the-german-english-CSV.png 836w, https://nerd-corner.com/wp-content/uploads/2023/05/reading-in-the-german-english-CSV-300x100.png 300w, https://nerd-corner.com/wp-content/uploads/2023/05/reading-in-the-german-english-CSV-768x257.png 768w" sizes="auto, (max-width: 825px) 100vw, 825px" /></p>
<p>Und hier das Endergebnis nach dem Mapping:</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1404 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/05/result-data-read-in-tfjs.png" alt="after mapping csv" width="515" height="99" srcset="https://nerd-corner.com/wp-content/uploads/2023/05/result-data-read-in-tfjs.png 528w, https://nerd-corner.com/wp-content/uploads/2023/05/result-data-read-in-tfjs-300x58.png 300w" sizes="auto, (max-width: 515px) 100vw, 515px" /></p>
<p>Obwohl Tfjs extra eine Funktion zum Einlesen der CSV bietet, hatte ich trotzdem schon mehr Ärger als in der Python Version. Ich habe auch auf die Schnelle keine Möglichkeit gefunden einen Datensatz im txt-Format einzulesen. Allerdings sind txt-Datensätze weit verbreitet!</p>
<h2>Daten vorbereiten</h2>
<p>Ich habe oft gesehen, dass für die Datenvorbereitung eine cleaning Funktion geschrieben wurde und der Output Satz auch einen Start und End Token erhalten hat. Daraufhin habe ich mich gefragt ob auch der Input Satz also der Encoder einen Start und End Token braucht. Im Rahmen von Sequenz-zu-Sequenz-Modellen benötigt der Encoder aber keine expliziten Start- und End-Token. Sein Zweck ist es, die Eingabesequenz so zu verarbeiten, wie sie ist, und eine Darstellung der Eingabe zu erstellen.</p>
<p>Der Decoder hingegen, der die Ausgabesequenz erzeugt, profitiert in der Regel von der Verwendung von Start- und End-Tokens. Diese Token helfen, den Anfang und das Ende der erzeugten Sequenz zu kennzeichnen. Die Verwendung von Start- und End-Token ist daher spezifisch für den Decoder. Während des Trainings enthält die Eingangssequenz des Decoders ein Start-Token am Anfang und schließt ein End-Token am Ende aus. Die Ausgabesequenz des Decoders enthält den End-Token und schließt den Start-Token aus. Auf diese Weise lernt das Modell, die richtige Ausgabesequenz auf der Grundlage der Eingabe zu erzeugen.</p>
<p>Bei der Erstellung von Übersetzungen mit dem trainierten Modell beginnt man mit dem Start-Token und generiert ein Token nach dem anderen, bis man auf den End-Token trifft oder eine maximale Sequenzlänge erreicht. Das Hinzufügen von Start- und End-Token zum Decoder-Satz verbessert die Leistung des NLP-Übersetzermodells. Es hilft bei der Festlegung klarer Sequenzgrenzen und unterstützt den Generierungsprozess, indem es angibt, wo die Übersetzung beginnt und endet.</p>
<h4>Zusammengefasst:</h4>
<ul>
<li>Encoder: Keine Notwendigkeit für Start- und End-Token. Verarbeitet die Eingabesequenz wie sie ist.</li>
<li>Decoder: Start- und End-Token sind hilfreich für die Generierung der Ausgabesequenz.</li>
</ul>
<p>Wir beginnen wieder mit dem einfachen Teil, nämlich Python. Wie wollen die eingelesenen Daten säubern. Das bedeutet alles in Kleinbuchstaben umwandeln und Zeichen, die nicht zum Alphabet gehören oder Satzzeichen sind, entfernen. Dafür brauchen wir die Regex Bibliothek (re).</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="atomic">import re

def clean(text):
    text = text.lower()  # lower case
    # remove any characters not a-z and ?!,'
    # please note that french has additional characters...I just simplified that
    text = re.sub(u"[^a-z!?',]", " ", text)
    return text


# apply cleaningFunctions to dataframe
data["english"] = data["english"].apply(lambda txt: clean(txt))
data["french"] = data["french"].apply(lambda txt: clean(txt))

# add &lt;start&gt; &lt;end&gt; token to decoder sentence (french)
data["french"] = data["french"].apply(lambda txt: f"&lt;start&gt; {txt} &lt;end&gt;")

print(data.sample(10))</pre>
<p>Ich habe hier vereinfacht. Da es sich um einen französischen Datensatz handelt sollte man eigentlich eine extra cleaning Funktion schreiben, die auch französische Buchstaben wie „ê“ berücksichtigt. Die  sample() Funktion dient nur zum veranschaulichen der Daten:</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1391 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/05/random-sample-of-cleaned-data.png" alt="tensorflow random sample of cleaned data" width="630" height="385" srcset="https://nerd-corner.com/wp-content/uploads/2023/05/random-sample-of-cleaned-data.png 639w, https://nerd-corner.com/wp-content/uploads/2023/05/random-sample-of-cleaned-data-300x184.png 300w" sizes="auto, (max-width: 630px) 100vw, 630px" /></p>
<p>In Tfjs ist der Ablauf absolut identisch. Ich habe eine cleanData() Funktion erstellt und den vorherigen Code modifiziert:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="js" data-enlighter-theme="atomic">function cleanData(text) {
  //if necessary also remove any characters not a-z and ?!,'
  return text.toLowerCase();
}</pre>
<pre class="EnlighterJSRAW" data-enlighter-language="js" data-enlighter-theme="atomic">const dataset = languageDataSet.map((record) =&gt; ({
   en: cleanData(record.en),
   de: "startToken " + cleanData(record.de) + " endToken",
 }));</pre>
<p>Das Ergebnis ist daher auch identisch zum Python Ansatz:</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1410 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/05/cleaned-up-tfjs-data.png" alt="cleaned up tfjs data" width="630" height="146" srcset="https://nerd-corner.com/wp-content/uploads/2023/05/cleaned-up-tfjs-data.png 639w, https://nerd-corner.com/wp-content/uploads/2023/05/cleaned-up-tfjs-data-300x69.png 300w" sizes="auto, (max-width: 630px) 100vw, 630px" /></p>
<p>Wenn die Wörter &#8222;start&#8220; und &#8222;end&#8220; Teil regulärer Sätze sind und nicht als spezielle Token zur Markierung von Anfang und Ende von Sequenzen verwendet werden, dann sollten sie bei der Tokenisierung auf keinen Fall durch entsprechende Indizes ersetzt werden. Bei der Tokenisierung ist es wichtig, spezielle Token zu wählen, die in den eigentlichen Eingabedaten wahrscheinlich nicht vorkommen werden. Dadurch wird sichergestellt, dass das Modell sie von normalen Wörtern unterscheiden kann und lernt, die entsprechenden Ausgabesequenzen zu erzeugen.</p>
<p>Wenn die Wörter &#8220; start&#8220; und &#8222;end&#8220; reguläre Wörter in den Eingabesätzen sind, sollte man in Erwägung ziehen, verschiedene spezielle Token zu verwenden, um den Anfang und das Ende von Sequenzen zu markieren. Eine gängige Wahl sind &#8220; &lt;start&gt;&#8220; und &#8222;&lt;end&gt;&#8220;. Durch die Verwendung spezieller Token, die wahrscheinlich nicht zum regulären Vokabular gehören, kann sichergestellt werden, dass sie vom Modell während des Trainings und der Generierung richtig identifiziert und verarbeitet werden können.</p>
<h5>Beispielsweise würden die tokenisierten Sequenzen wie folgt aussehen:</h5>
<p>Decoder Eingabe: [&#8222;&lt;start&gt;&#8220;, &#8222;hallo&#8220;, &#8222;welt&#8220;]<br />
Decoder Ausgabe: [&#8222;hallo&#8220;, &#8222;welt&#8220;, &#8222;&lt;end&gt;&#8220;]</p>
<h5>Daher nachfolgendes VERMEIDEN:</h5>
<p>Decoder Eingabe: [&#8222;start&#8220;, &#8222;hallo&#8220;, &#8222;welt&#8220;]<br />
Decoder Ausgabe: [&#8222;hallo&#8220;, &#8222;welt&#8220;, &#8222;end&#8220;]</p>
<h2>Tokenisierung</h2>
<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="atomic"># tokenization
import tensorflow as tf
from tensorflow import keras
from keras.preprocessing.text import Tokenizer
import numpy as np

# english tokenizer
english_tokenize = Tokenizer(filters='#$%&amp;()*+,-./:;&lt;=&gt;@[\\]^_`{|}~\t\n')
english_tokenize.fit_on_texts(data["english"])
num_encoder_tokens = len(english_tokenize.word_index)+1
# print(num_encoder_tokens)
encoder = english_tokenize.texts_to_sequences(data["english"])
# print(encoder[:5])
max_encoder_sequence_len = np.max([len(enc) for enc in encoder])
# print(max_encoder_sequence_len)

# french tokenizer
french_tokenize = Tokenizer(filters="#$%&amp;()*+,-./:;&lt;=&gt;@[\\]^_`{|}~\t\n")
french_tokenize.fit_on_texts(data["french"])
num_decoder_tokens = len(french_tokenize.word_index)+1
# print(num_decoder_tokens)
decoder = french_tokenize.texts_to_sequences(data["french"])
# print(decoder[:5])
max_decoder_sequence_len = np.max([len(dec) for dec in decoder])
# print(max_decoder_sequence_len)

</pre>
<p>Dieser Code führt Tokenisierung und Sequenzvorverarbeitung mit der <strong>Tokenizer</strong> Klasse in TensorFlow durch.</p>
<ol>
<li><strong>english_tokenize = Tokenizer(filters=&#8217;#$%&amp;()*+,-./:;&lt;=&gt;@[\\]^_`{|}~\t\n&#8216;)</strong> Initialisiert ein Tokenizer-Objekt für englische Sätze. Der Parameter `filters` gibt Zeichen an, die während der Tokenisierung herausgefiltert werden sollen. Wir haben die Daten im Cleaning Prozess bereits gefiltert, es ist daher eigentlich nicht notwendig hier nochmal zu filtern.</li>
<li><strong>english_tokenize.fit_on_texts(data[&#8222;english&#8220;])</strong> Aktualisiert das interne Vokabular des Tokenizers basierend auf den englischen Sätzen in der Variable <strong>data</strong>. Jedem Wort im Vokabular wird ein eindeutiger Index zugewiesen.</li>
<li><strong>num_encoder_tokens = len(english_tokenize.word_index) + 1</strong> Bestimmt die Anzahl der eindeutigen Token (Wörter) im englischen Vokabular. Das Attribut <strong>word_index</strong> des Tokenizers gibt ein Wörterbuch zurück, das Wörter auf ihre jeweiligen Indizes abbildet.</li>
<li><strong>encoder = english_tokenize.texts_to_sequences(data[&#8222;english&#8220;])</strong> Konvertiert die englischen Sätze in der Variablen <strong>data</strong> in Sequenzen von Token-Indizes unter Verwendung des Tokenizers. Jeder Satz wird durch eine Folge von Ganzzahlen ersetzt, die die entsprechenden Wörter darstellen.</li>
<li><strong>max_encoder_sequence_len = np.max([len(enc) for enc in encoder])</strong> Berechnet die maximale Länge (Anzahl der Token) unter allen kodierten Sequenzen. Sie verwendet die Funktion <strong>max</strong> von NumPy, um den maximalen Wert in einem Listenverständnis zu finden.</li>
</ol>
<p>Diese Schritte helfen, die Sätze für die weitere Verarbeitung in einem NLP-Modell vorzubereiten. Das ist für beide Sprachen notwendig!</p>
<p>Die Sätze wurden jetzt in Token umgewandelt, anschließend in Sequenzen von Token-Indizes konvertiert und die maximale Sequenzlänge bestimmt. Ein Beispielsatz aus dem Datensatz sieht jetzt so aus: [[148], [252], [59], [14], [111]]. Hierbei könnte die 148 für „I“, 252 für „am“, 59 für „very“, 14 für „hungry“ und 111 für „now“ stehen.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="atomic">idx_2_txt_decoder = {k: i for i, k in french_tokenize.word_index.items()}
# print(idx_2_txt_decoder)
idx_2_txt_encoder = {k: i for i, k in english_tokenize.word_index.items()}
# print(idx_2_txt_encoder)

idx_2_txt_decoder[0] = "&lt;pad&gt;"
idx_2_txt_encoder[0] = "&lt;pad&gt;"</pre>
<p>Der Codeschnipsel <strong>idx_2_txt_encoder = {k: i for i, k in english_tokenize.word_index.items()} </strong>erstellt ein Wörterbuch Verzeichnis <strong>idx_2_txt_encoder</strong>, das Token-Indizes den entsprechenden Wörtern im englischen Vokabular zuordnet: <strong>{k: i for i, k in english_tokenize.word_index.items()}</strong> ist ein Verzeichnis, das über die Schlüssel-Wert-Paare in <strong>english_tokenize.word_index</strong> iteriert. Bei jeder Iteration steht der Key (<strong>k</strong>) für ein Wort im Vokabular, und der Wert (<strong>i</strong>) für den entsprechenden Index. Das Verständnis erstellt ein neues Wörterbuch, dessen Keys die Indizes (<strong>i</strong>) und die Werte die Wörter (<strong>k</strong>) sind.</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1395 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/05/index2textencoder-printed.png" alt="index 2 tokenizer dicitonary sample" width="1730" height="253" srcset="https://nerd-corner.com/wp-content/uploads/2023/05/index2textencoder-printed.png 1742w, https://nerd-corner.com/wp-content/uploads/2023/05/index2textencoder-printed-300x44.png 300w, https://nerd-corner.com/wp-content/uploads/2023/05/index2textencoder-printed-1024x150.png 1024w, https://nerd-corner.com/wp-content/uploads/2023/05/index2textencoder-printed-768x112.png 768w, https://nerd-corner.com/wp-content/uploads/2023/05/index2textencoder-printed-1536x225.png 1536w" sizes="auto, (max-width: 1730px) 100vw, 1730px" /></p>
<p>Das resultierende <strong>idx_2_txt_encoder </strong>&#8211; Wörterbuch ermöglicht es, das Wort, das einem bestimmten Index entspricht, im englischen Wortschatz nachzuschlagen. <strong>english_tokenize.word_index </strong>würde übrigens die Anzeigen genau vertauschen. Hier wäre der Key das Wort sein und der Wert der Index. Die zweite Zeile, <strong>idx_2_txt_encoder[0] = &#8222;&lt;pad&gt;&#8220;</strong>, fügt dem Wörterbuch einen speziellen Eintrag hinzu. Hier wird das Wort &#8222;&lt;pad&gt;&#8220; dem Index &#8222;0&#8220; zugeordnet, um einen Auffüllungs-Token anzugeben, der beim Padding von Sequenzen verwendet wird.</p>
<p>Anschließend sollte man das Wörterbuch Verzeichnis abspeichern, denn später wenn das Modell trainiert wurde und eingesetzt wird, werden die Übersetzungen des Modells ebenfalls eine Reihe von Indizes sein, die mit Hilfe des Wörterbuchs in lesbare Sätze zurücktransformiert werden.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="atomic"># Saving the dicitionaries
pickle.dump(idx_2_txt_encoder, open("./saves/idx_2_word_input.txt", "wb"))
pickle.dump(idx_2_txt_decoder, open("./saves/idx_2_word_target.txt", "wb"))</pre>
<p>Der gleiche Ablauf wie in Python lässt sich auch für die NLP Anwendung in Tensorflow.js konstruieren. Selbstverständlich benötigt man etwas mehr Codezeilen und der Arbeitsaufwand ist insgesamt höher. Die erste Hürde hierbei ist der Tokenizer. Leider besitzt Tfjs im Gegensatz zu Tensorflow (Python) keinen eigenen Tokenizer. Nach ausgiebiger Recherche fand ich zum Glück den natural.WordTokenizer. Hierbei möchte ich darauf hinweisen, dass definitv ein Node.js Projekt benötigt wird. Tfjs lässt sich zwar über einen &lt;script&gt; Tag einbinden, der natural.WordTokenizer, dagegen aber nicht!</p>
<p>Ein weiterer wichtiger Punkt ist, dass der WordTokenizer &#8222;&lt;&#8220; und &#8222;&gt;&#8220; entfernt. Aus einem Output Satz &#8222;&lt;start&gt; ich esse &lt;end&gt;&#8220; wird daher einfach [&#8217;start&#8216;, &#8218;ich&#8216;, &#8218;esse&#8216;, &#8218;end&#8216;]. Somit ist der &#8222;&lt;start&gt;&#8220; und &#8222;&lt;end&gt;&#8220; Token nicht mehr klar erkennbar! Ich habe sie daher im JS Code von Anfang ersetzt durch &#8222;startToken&#8220; und &#8222;endToken&#8220;.</p>
<p>Zunächst tokenisieren wir wieder jeden einzelnen Satz aus dem Datensatz und erstellen anschließend ein Vokabelverzeichnis für jede der beiden Sprachen. Abschließend ersetzen wir alle Wörter durch die Indizes aus dem Vokabelverzeichnis:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="js" data-enlighter-theme="atomic">const natural = require("natural");

function tokenize(data) {
  const tokenizer = new natural.WordTokenizer();

  enData = data.map((row) =&gt; tokenizer.tokenize(row.en));
  deData = data.map((row) =&gt; tokenizer.tokenize(row.de));

  const enVocabulary = new Map();
  const deVocabulary = new Map();

  // Insert &lt;pad&gt; at index 0
  enVocabulary.set("&lt;pad&gt;", 0);
  deVocabulary.set("&lt;pad&gt;", 0);

  const fillVocabulary = (langData, vocabMap) =&gt; {
    langData.forEach((sentence) =&gt; {
      sentence.forEach((word) =&gt; {
        if (!vocabMap.has(word)) {
          const newIndex = vocabMap.size;
          vocabMap.set(word, newIndex);
        }
      });
    });
  };

  fillVocabulary(enData, enVocabulary);
  fillVocabulary(deData, deVocabulary);

  // Replace words with indexes
  const indexedEnData = enData.map((element) =&gt;
    element.map((word) =&gt; enVocabulary.get(word))
  );
  const indexedDeData = deData.map((element) =&gt;
    element.map((word) =&gt; deVocabulary.get(word))
  );

  return { en: indexedEnData, de: indexedDeData };
}</pre>
<p>Um später die Resultate unseres Modells wieder in Wörter umwandeln zu können und um auch später im realen Anwendungsfall das Modell nutzen zu können, speichern wir die beiden Vokabelverzeichnisse ab. Ich habe dabei die Key und Value Paare getauscht, aber letztlich ist das nicht zwingend erforderlich:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="js" data-enlighter-theme="atomic">const fs = require("fs");

// store the input and output key value pairs
  fs.writeFileSync(
    "vocabulary/inputVocableSet.json",
    JSON.stringify(switchKeysAndValues(Object.fromEntries(enVocabulary)))
  );
  fs.writeFileSync(
    "vocabulary/outputVocableSet.json",
    JSON.stringify(switchKeysAndValues(Object.fromEntries(deVocabulary)))
  );

function switchKeysAndValues(obj) {
  const switchedObj = {};
  for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      const value = obj[key];
      switchedObj[value] = key;
    }
  }
  return switchedObj;
}</pre>
<p>Als Resultat erhalten wir ein JSON Objekt mit unseren Vokabeln:</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1411 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/06/excerpt-of-the-stored-json-map.png" alt="excerpt of the stored json map" width="240" height="553" srcset="https://nerd-corner.com/wp-content/uploads/2023/06/excerpt-of-the-stored-json-map.png 249w, https://nerd-corner.com/wp-content/uploads/2023/06/excerpt-of-the-stored-json-map-130x300.png 130w" sizes="auto, (max-width: 240px) 100vw, 240px" /></p>
<p>Anschließend returnen wir das Ergebnis unserer Funktion:</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1412 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/06/result-after-tokenization.png" alt="result after tokenization" width="890" height="679" srcset="https://nerd-corner.com/wp-content/uploads/2023/06/result-after-tokenization.png 895w, https://nerd-corner.com/wp-content/uploads/2023/06/result-after-tokenization-300x229.png 300w, https://nerd-corner.com/wp-content/uploads/2023/06/result-after-tokenization-768x586.png 768w" sizes="auto, (max-width: 890px) 100vw, 890px" /></p>
<h2>Dateien zum Herunterladen</h2>
<ul>
<li><a  data-e-Disable-Page-Transition="true" class="download-link" title="" href="https://nerd-corner.com/de/download/1432/?tmstv=1755909777" rel="nofollow" id="download-link-1432" data-redirect="false" >
	NLP Tensorflow.js code (model has an error!)</a>
</li>
</ul>
<p>The post <a href="https://nerd-corner.com/de/nlp-anwendung-tensorflow-js-vs-tensorflow-python-teil-1/">NLP Anwendung: Tensorflow.js vs Tensorflow Python &#8211; Teil 1</a> appeared first on <a href="https://nerd-corner.com/de">Nerd Corner</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://nerd-corner.com/de/nlp-anwendung-tensorflow-js-vs-tensorflow-python-teil-1/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Tensorflow.js oder Tensorflow nutzen?</title>
		<link>https://nerd-corner.com/de/tensorflow-js-oder-tensorflow-nutzen/</link>
					<comments>https://nerd-corner.com/de/tensorflow-js-oder-tensorflow-nutzen/#comments</comments>
		
		<dc:creator><![CDATA[Nerds]]></dc:creator>
		<pubDate>Thu, 18 May 2023 13:48:21 +0000</pubDate>
				<category><![CDATA[Software-DE]]></category>
		<category><![CDATA[CUDA]]></category>
		<category><![CDATA[Deep Learning]]></category>
		<category><![CDATA[GPU]]></category>
		<category><![CDATA[Machine Learning]]></category>
		<category><![CDATA[Natural Language Processing]]></category>
		<category><![CDATA[NLP]]></category>
		<category><![CDATA[Programmierung]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[software]]></category>
		<category><![CDATA[Software Vergleich]]></category>
		<category><![CDATA[Tensorflow]]></category>
		<category><![CDATA[Tensorflow Anleitung]]></category>
		<category><![CDATA[TensorFlow GPU]]></category>
		<category><![CDATA[Tensorflow python]]></category>
		<category><![CDATA[Tensorflow.js]]></category>
		<category><![CDATA[Tensorflow.js vs Tensorflow]]></category>
		<category><![CDATA[TPU]]></category>
		<guid isPermaLink="false">https://nerd-corner.com/?p=1380</guid>

					<description><![CDATA[<p>In der Welt des maschinellen Lernens und des Deep Learnings gibt es eine Vielzahl von Tools und Bibliotheken, die Entwicklern dabei helfen, fortschrittliche Modelle zu &#8230; </p>
<p>The post <a href="https://nerd-corner.com/de/tensorflow-js-oder-tensorflow-nutzen/">Tensorflow.js oder Tensorflow nutzen?</a> appeared first on <a href="https://nerd-corner.com/de">Nerd Corner</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>In der Welt des maschinellen Lernens und des Deep Learnings gibt es eine Vielzahl von Tools und Bibliotheken, die Entwicklern dabei helfen, fortschrittliche Modelle zu erstellen und trainieren. Zwei beliebte Optionen sind Tensorflow.js und das ursprüngliche Tensorflow, die jeweils für JavaScript und Python entwickelt wurden. Auf Grund eines Projekts bei dem ich mittels NLP (Natural Language Processing) automatisiert Standardsprachtexte in Dialekt umwandeln will habe ich mich näher mit den beiden Optionen beschäftigt. In diesem Blogartikel werde ich die Unterschiede und Vorzüge von Tensorflow.js und Tensorflow (Python) beleuchten. Anschließend sollte jeder in der Lage sein um entscheiden zu können, ob er Tensorflow.js oder Tensorflow (Python) nutzen möchte.</p>
<p><em><strong>Das könnte dich auch interessieren:</strong> <a href="https://nerd-corner.com/de/tensorflow-gpu-aktivieren-unter-windows/">Wie man in Windows die GPU für TensorFlow aktiviert</a></em></p>
<h2>Sprache und Umgebung</h2>
<p>Tensorflow.js wurde speziell für JavaScript entwickelt, was bedeutet, dass sich Modelle direkt in JavaScript-Code implementieren lassen. Dies ermöglicht die nahtlose Integration in Webanwendungen und die Ausführung der Modelle im Browser ohne zusätzliche Serverinfrastruktur.</p>
<p>Tensorflow Python bietet eine umfassende Python-Bibliothek für maschinelles Lernen. Python ist eine weit verbreitete Programmiersprache im Bereich des maschinellen Lernens und bietet eine Vielzahl von Bibliotheken und Frameworks, die die Entwicklung und den Einsatz von Modellen erleichtern.</p>
<h2>Zielplattform</h2>
<p>Eine der größten Stärken von Tensorflow.js ist die Möglichkeit, Modelle direkt im Browser auszuführen. Dies ermöglicht es Entwicklern, interaktive Webanwendungen mit maschinellem Lernen zu erstellen, ohne dass der Nutzer zusätzliche Software installieren oder externe Serveranfragen senden muss.</p>
<p>Tensorflow Python hingegen ermöglicht die Entwicklung und Ausführung von Modellen auf verschiedenen Plattformen, einschließlich Desktop-Computern, Servern und mobilen Geräten. Es bietet eine breite Palette an Funktionen und unterstützt fortschrittliche Techniken wie das Trainieren von Modellen auf GPUs oder TPUs. TPU steht für „Tensor Processing Unit“. Das sind anwendungsspezifische Chips um Anwendungen im Rahmen von maschinellem Lernen zu beschleunigen</p>
<h2>Community</h2>
<p>Wenn man ehrlich ist die Tensorflow.js Community einfach zu klein. Bei vielen Fragen hat man das Gefühl man leistet hier „Pioniersarbeit“, was gerade für Anfänger nicht zu empfehlen ist. Zur Verdeutlichung: Es gibt 80 Mal mehr StackOverflow Fragen zu TensorFlow als zu TensorFlow.js. Je nach Anwendungsfall existieren aber mittlerweile auch für TensorFlow.js eine Reihe von Ressourcen, Tutorials und Beispielen, die bei der Entwicklung mit Tensorflow.js helfen können. Da JavaScript eine weit verbreitete Sprache ist, kann man auch auf eine große Menge an generellen Webentwicklungsressourcen zurückgreifen, die bei der Arbeit mit Tensorflow.js helfen können.</p>
<p>Tensorflow Python dagegen profitiert von einer großen und aktiven Community, die regelmäßig neue Modelle, Techniken und Ressourcen entwickelt und veröffentlicht. Es gibt eine Vielzahl von Tutorials, Foren und Open-Source-Projekten, die Ihnen helfen können, Ihre maschinellen Lernprojekte voranzutreiben. Die Python-Community ist für ihre Unterstützung und Zusammenarbeit bekannt, was den Einstieg in Tensorflow Python erleichtern kann.</p>
<h2>Bibliotheken</h2>
<p>In vielen Fällen und speziell bei meinem NLP Projekt ein K.O Kriterium für Tensorflow.js waren die Bibliotheken. Tensorflow.js hat im Vergleich zu Tensorflow Python viel weniger Bibliotheken. Das liegt aus meiner Sicht an folgenden 3 Punkten:</p>
<ol>
<li>Entwicklungsstand: Tensorflow Python existiert seit mehreren Jahren und hat eine lange Entwicklungszeit hinter sich. Während dieser Zeit wurden zahlreiche Erweiterungen, Module und zusätzliche Bibliotheken entwickelt, die speziell für Tensorflow Python entwickelt wurden. Tensorflow.js hingegen ist eine vergleichsweise neuere Technologie und befindet sich möglicherweise noch in einem früheren Entwicklungsstadium. Daher hat Tensorflow.js möglicherweise weniger Bibliotheken und Erweiterungen, die speziell für diese Plattform entwickelt wurden.</li>
</ol>
<p>&nbsp;</p>
<ol start="2">
<li>Zielplattform: Tensorflow Python richtet sich an eine breite Palette von Plattformen, einschließlich Desktop-Computern, Servern und mobilen Geräten. Daher gibt es eine Vielzahl von spezialisierten Bibliotheken und Erweiterungen für verschiedene Anwendungsfälle und Hardwarekonfigurationen. Tensorflow.js hingegen zielt darauf ab, Modelle direkt im Browser auszuführen. Daher sind die Funktionen und Bibliotheken von Tensorflow.js auf die Anforderungen von Webanwendungen und die begrenzte Ressourcenverfügbarkeit im Browser optimiert.</li>
</ol>
<p>&nbsp;</p>
<ol start="3">
<li>Kompatibilität: Tensorflow.js basiert auf JavaScript, einer Sprache, die in erster Linie für Webentwicklung verwendet wird. Obwohl JavaScript eine große Entwicklergemeinschaft und viele bestehende Bibliotheken und Frameworks hat, sind nicht alle davon direkt mit Tensorflow.js kompatibel. Daher kann es sein, dass nicht alle verfügbaren Bibliotheken und Erweiterungen für Tensorflow Python auch für Tensorflow.js zur Verfügung stehen.</li>
</ol>
<p>Es ist jedoch wichtig anzumerken, dass Tensorflow.js stetig weiterentwickelt wird und die Bibliothek und die verfügbaren Erweiterungen mit der Zeit wachsen können. Die Community rund um Tensorflow.js arbeitet daran, das Ökosystem zu erweitern und neue Bibliotheken sowie Tools bereitzustellen, um den Funktionsumfang zu verbessern und die Möglichkeiten von Tensorflow.js zu erweitern.</p>
<h2>Plattformunterstützung</h2>
<p>TensorFlow, das in C++ geschrieben wurde, ist eine plattformübergreifende Bibliothek, die auf verschiedenen Betriebssystemen wie Windows, macOS und Linux unterstützt wird. Es kann sowohl für die Entwicklung und Ausführung von Modellen auf Servern als auch auf Desktop-Computern verwendet werden.</p>
<p>TensorFlow<span style="font-size: 1.125rem;"> </span><span style="font-size: 1.125rem;">unterstützt</span><span style="font-size: 1.125rem;"> daher </span><span style="font-size: 1.125rem;">eine breitere Palette von Plattformen als TensorFlow.js, da TensorFlow.js hauptsächlich auf JavaScript-Umgebungen wie dem Browser und Node.js ausgerichtet ist.</span></p>
<h2>Trainieren großer Modelle</h2>
<p>TensorFlow kann sehr große Modelle trainieren und verarbeiten, während TensorFlow.js aufgrund der Leistungseinschränkungen von JavaScript-Engines auf kleinere Modelle beschränkt ist.</p>
<p>JavaScript-Engines sind im Vergleich zu spezialisierten Machine-Learning-Frameworks und Hardwarebeschleunigern wie GPUs oder TPUs weniger leistungsstark. Dies bedeutet, dass TensorFlow.js aufgrund der begrenzten Rechenleistung und des begrenzten Speichers von JavaScript-Engines in der Regel für kleinere Modelle geeignet ist.</p>
<p>Daher wird TensorFlow für Projekte empfohlen, bei denen große Modelle trainiert oder komplexe Berechnungen durchgeführt werden müssen, während TensorFlow.js besser für Anwendungen geeignet ist, die kleinere Modelle verwenden und in Webbrowsern oder JavaScript-Umgebungen ausgeführt werden sollen.</p>
<h2>Warum TensorFlow.js clientseitig im Browser nutzen?</h2>
<p>Da wäre das Thema Geschwindigkeit. Da man keine Daten an einen entfernten Server senden muss, erfolgt die Klassifizierung schneller. Zudem hat man direkten Zugriff auf die Sensoren wie Kamera, Mikrofon, GPS usw.</p>
<p>Außerdem ist in vielen Ländern der Datenschutz ein wichtiger Punkt. Man kann Daten auf dem eigenen Rechner trainieren und klassifizieren, ohne sie an einen externen Webserver senden zu müssen. Das kann erforderlich sein, um Datenschutzgesetze wie die DSGVO einzuhalten oder wenn man die Daten nicht an Dritte weitergeben möchte.</p>
<p>Mit einem Klick kann jeder auf der Welt über einen Link auf die Anwendung zugreifen und sie nutzen ohne, dass ein komplexes Setup mit Servern und spezieller Hardware wie Grafikkarten erforderlich ist.</p>
<p>Abschließend sollte man bei ML auch die Kosten im Blick behalten. Man muss lediglich für das hosten des Clients zahlen. Das ist deutlich günstiger als die dauerhafte Aufrechterhaltung eines eigenen Servers.</p>
<h2>Fazit Tensorflow.js oder Tensorflow nutzen?</h2>
<p>Insgesamt kann man die Frage Tensorflow.js oder Tensorflow vereinfacht dadurch beantworten, dass Tensorflow mit Python aufgrund seiner breiten Akzeptanz und großen <a href="https://www.tensorflow.org/community" target="_blank" rel="noopener">Community</a> nahezu IMMER die bessere Wahl ist. Nichtsdestotrotz gewinnt Tensorflow.js zunehmend an Bedeutung und wird überwiegend von Entwicklern genutzt, die Webanwendungen mit maschinellem Lernen clientseitig entwickeln möchten.</p>
<p>Das heißt, wenn du Anwendungen erstellen möchtest, die im Webbrowser laufen sollen, ist TensorFlow.js eigentlich die bessere Wahl. Wenn du jedoch Anwendungen erstellen möchtest, die auf einem Server oder einem Desktop-Computer laufen sollen, ist TensorFlow die bessere Option. Außerdem ist die Python Version viel besser geeignet, wenn du mit leistungsstarken Geräten wie GPUs arbeiten möchtest.</p>
<p>&nbsp;</p>
<p>The post <a href="https://nerd-corner.com/de/tensorflow-js-oder-tensorflow-nutzen/">Tensorflow.js oder Tensorflow nutzen?</a> appeared first on <a href="https://nerd-corner.com/de">Nerd Corner</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://nerd-corner.com/de/tensorflow-js-oder-tensorflow-nutzen/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Tensorflow GPU aktivieren unter Windows</title>
		<link>https://nerd-corner.com/de/tensorflow-gpu-aktivieren-unter-windows/</link>
					<comments>https://nerd-corner.com/de/tensorflow-gpu-aktivieren-unter-windows/#comments</comments>
		
		<dc:creator><![CDATA[Nerds]]></dc:creator>
		<pubDate>Tue, 09 May 2023 21:22:10 +0000</pubDate>
				<category><![CDATA[Software-DE]]></category>
		<category><![CDATA[Visual Studio-DE]]></category>
		<category><![CDATA[Anleitung]]></category>
		<category><![CDATA[Deep Learning]]></category>
		<category><![CDATA[GPU]]></category>
		<category><![CDATA[Grafikkarte]]></category>
		<category><![CDATA[Machine Learning]]></category>
		<category><![CDATA[ML]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Schritt für Schritt Anweisung]]></category>
		<category><![CDATA[Tensorflow]]></category>
		<category><![CDATA[Tensorflow Anleitung]]></category>
		<category><![CDATA[TensorFlow GPU]]></category>
		<category><![CDATA[Windows]]></category>
		<guid isPermaLink="false">https://nerd-corner.com/?p=1364</guid>

					<description><![CDATA[<p>Ich arbeite aktuell an einem Hobbyprojekt bei dem ich mittels KI deutsche Sätze ins Bairische übersetzen möchte, wie beispielsweise auf der Webseite Dialektl.com. Ich arbeite &#8230; </p>
<p>The post <a href="https://nerd-corner.com/de/tensorflow-gpu-aktivieren-unter-windows/">Tensorflow GPU aktivieren unter Windows</a> appeared first on <a href="https://nerd-corner.com/de">Nerd Corner</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Ich arbeite aktuell an einem Hobbyprojekt bei dem ich mittels KI deutsche Sätze ins Bairische übersetzen möchte, wie beispielsweise auf der Webseite <a href="https://dialektl.com/" target="_blank" rel="noopener">Dialektl.com</a>. Ich arbeite hierfür mit Tensorflow, eine Open-Source-Plattform für maschinelles Lernen und Deep Learning. Tensorflow ist eine der am häufigsten verwendeten Bibliotheken für Deep Learning, da sie eine breite Palette von Funktionen und eine sehr aktive Entwicklergemeinschaft bietet. Für fast alle Machine Learning Modelle ist der Trainingsprozess äußert rechenaufwendig. Um die Geschwindigkeit des Trainingsprozesses zu erhöhen empfiehlt es sich die Grafikkarte (GPU) des Computers anstelle des Prozessors (CPU) zu verwenden.</p>
<p>Mein naiver Gedanke war, dass Tensorflow automatisch die GPU benutzen würde. Man muss allerdings erst die nachfolgende Schritt für Schritt Anleitung befolgen, damit Tensorflow die GPU überhaupt erkennt und nutzt.</p>
<p><em><strong>Das könnte dich auch interessieren:</strong> <a href="https://nerd-corner.com/de/tensorflow-js-oder-tensorflow-nutzen/">Tensorflow.js oder Tensorflow (Python) nutzen?</a></em></p>
<p><strong>DISCLAIMER: Ab Version 2.11 unterstütz Tensorflow keine GPUs mehr unter Windows! Entweder wechselt man das Betriebssystem oder downgraded Tensorflow auf Version 2.10. Außerdem wird eine Grafikkarte von NVIDIA benötigt!</strong></p>
<h3>1. Überprüfen der gewünschten CUDA und cuDNN Versionen</h3>
<p>Zunächst sollte man herausfinden welche CUDA und cuDNN Version von TensorFlow benötigt wird. Auf der Webseite werden alle Versionen von Tensorflow und die gewünschte CUDA Versionen bzw. cuDNN Versionen aufgelistet: <a href="https://www.tensorflow.org/install/source_windows#gpu">https://www.tensorflow.org/install/source_windows#gpu</a></p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1372 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/05/tensorflow_requirements.png" alt="tensorflow requirements" width="1120" height="562" srcset="https://nerd-corner.com/wp-content/uploads/2023/05/tensorflow_requirements.png 1128w, https://nerd-corner.com/wp-content/uploads/2023/05/tensorflow_requirements-300x151.png 300w, https://nerd-corner.com/wp-content/uploads/2023/05/tensorflow_requirements-1024x514.png 1024w, https://nerd-corner.com/wp-content/uploads/2023/05/tensorflow_requirements-768x385.png 768w" sizes="auto, (max-width: 1120px) 100vw, 1120px" /></p>
<h2>2. Überprüfen der eigenen Grafikkarte</h2>
<p>Wie bereits im Disclaimer erwähnt muss die Grafikkarte von NVIDIA sein. Auf der Webseite <a href="https://developer.nvidia.com/cuda-gpus">https://developer.nvidia.com/cuda-gpus</a> kann man seine eigene GPU suchen und die „Compute Capability“ überprüfen. Mindestvoraussetzung für Tensorflow ist ein Wert von 3.5, der aber von allen aktuellen Grafikkarten erfüllt wird.</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1371 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/05/cuda-architectures.png" alt="cuda architectures" width="1120" height="158" srcset="https://nerd-corner.com/wp-content/uploads/2023/05/cuda-architectures.png 1129w, https://nerd-corner.com/wp-content/uploads/2023/05/cuda-architectures-300x42.png 300w, https://nerd-corner.com/wp-content/uploads/2023/05/cuda-architectures-1024x144.png 1024w, https://nerd-corner.com/wp-content/uploads/2023/05/cuda-architectures-768x108.png 768w" sizes="auto, (max-width: 1120px) 100vw, 1120px" /></p>
<h2>3. Installieren der neusten NVIDIA-Treiber</h2>
<p>Um die GPU für TensorFlow zu aktivieren, müssen die neuesten NVIDIA-Treiber installiert sein. Einfach auf die NVIDIA-Website gehen den neuesten Treiber die eigene Grafikkarte herunterladen.</p>
<h2>4. Installieren von Visual Studio (optional)</h2>
<p>In TensorFlow wurden einige Teile der Bibliothek in C++ geschrieben, um die Leistung zu maximieren. Daher kann die Installation von Visual Studio dazu beitragen, die Kompatibilität und Leistung von TensorFlow zu verbessern: <a style="font-size: 1.125rem; color: midnightblue; outline: 0px;" href="https://visualstudio.microsoft.com/de/vs/">https://visualstudio.microsoft.com/de/vs/</a></p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1370 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/05/vs-studio-download.png" alt="Visual studio installer" width="690" height="304" srcset="https://nerd-corner.com/wp-content/uploads/2023/05/vs-studio-download.png 694w, https://nerd-corner.com/wp-content/uploads/2023/05/vs-studio-download-300x132.png 300w" sizes="auto, (max-width: 690px) 100vw, 690px" /></p>
<p>Es reicht hier einzelne Komponenten auszuwählen. Ich habe hier alles mit C++ in „Compiler, Buildtools und Runtimes“ ausgewählt und zusätzlich noch MS Build, was wiederum ein paar weitere Komponenten automatisch installiert. Insgesamt aber trotzdem über 25 GB!</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1369 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/05/VS-studio-components.png" alt="choose components in visual studio" width="880" height="751" srcset="https://nerd-corner.com/wp-content/uploads/2023/05/VS-studio-components.png 885w, https://nerd-corner.com/wp-content/uploads/2023/05/VS-studio-components-300x256.png 300w, https://nerd-corner.com/wp-content/uploads/2023/05/VS-studio-components-768x655.png 768w" sizes="auto, (max-width: 880px) 100vw, 880px" /></p>
<h2>5. Installieren von CUDA-Toolkit</h2>
<p>Das CUDA-Toolkit ist ein Toolkit für die Entwicklung von CUDA-Anwendungen, das von NVIDIA bereitgestellt wird. TensorFlow benötigt CUDA, um auf der GPU ausgeführt zu werden. Die in Schritt 1 verlangte Version des CUDA-Toolkits einfach von der NVIDIA-Website herunterladen und installieren: <a href="https://developer.nvidia.com/cuda-downloads">https://developer.nvidia.com/cuda-downloads</a></p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1368 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/05/Cuda-toolkit-download.png" alt="download cuda toolkit" width="1130" height="889" srcset="https://nerd-corner.com/wp-content/uploads/2023/05/Cuda-toolkit-download.png 1135w, https://nerd-corner.com/wp-content/uploads/2023/05/Cuda-toolkit-download-300x236.png 300w, https://nerd-corner.com/wp-content/uploads/2023/05/Cuda-toolkit-download-1024x806.png 1024w, https://nerd-corner.com/wp-content/uploads/2023/05/Cuda-toolkit-download-768x604.png 768w" sizes="auto, (max-width: 1130px) 100vw, 1130px" /></p>
<h2>6. Installieren der cuDNN-Bibliotheken</h2>
<p>cuDNN ist eine Bibliothek von Deep-Learning-Primitiven, die von NVIDIA bereitgestellt wird. TensorFlow benötigt zusätzlich cuDNN, um auf der GPU ausgeführt zu werden. cuDNN ist kostenlos, aber man muss einen Account als NVIDIA Developer erstellen: <a href="https://developer.nvidia.com/rdp/cudnn-download">https://developer.nvidia.com/rdp/cudnn-download</a></p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1367 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/05/cuDNN-download.png" alt="cuDNN download" width="1710" height="875" srcset="https://nerd-corner.com/wp-content/uploads/2023/05/cuDNN-download.png 1717w, https://nerd-corner.com/wp-content/uploads/2023/05/cuDNN-download-300x154.png 300w, https://nerd-corner.com/wp-content/uploads/2023/05/cuDNN-download-1024x524.png 1024w, https://nerd-corner.com/wp-content/uploads/2023/05/cuDNN-download-768x393.png 768w, https://nerd-corner.com/wp-content/uploads/2023/05/cuDNN-download-1536x786.png 1536w" sizes="auto, (max-width: 1710px) 100vw, 1710px" /></p>
<p>Nach dem Download muss der Inhalt entpackt werden. Der Inhalt wirdin dem Programme Ordner in „NVIDIA GPU Computing Toolkit“ hinein verschoben. Nach dem Verschieben den Dateipfad des „bin“ Ordners kopieren.</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1366 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/05/unzip-cuDNN.png" alt="unzip cuDNN" width="670" height="226" srcset="https://nerd-corner.com/wp-content/uploads/2023/05/unzip-cuDNN.png 673w, https://nerd-corner.com/wp-content/uploads/2023/05/unzip-cuDNN-300x101.png 300w" sizes="auto, (max-width: 670px) 100vw, 670px" /></p>
<h2>7. PATH Variable setzen</h2>
<p>Öffne die Umgebungsvariablen in dem du den Begriff einfach in die Windows Suche eingibst. Dann die Systemvariable „path“ bearbeiten und einen neuen Eintrag hinzufügen mit dem Dateipfad des „bin“ Ordners aus dem vorherigem Schritt.</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1365 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/05/path-variable.png" alt="Set path variable for tensorflow gpu" width="1220" height="203" srcset="https://nerd-corner.com/wp-content/uploads/2023/05/path-variable.png 1223w, https://nerd-corner.com/wp-content/uploads/2023/05/path-variable-300x50.png 300w, https://nerd-corner.com/wp-content/uploads/2023/05/path-variable-1024x170.png 1024w, https://nerd-corner.com/wp-content/uploads/2023/05/path-variable-768x127.png 768w" sizes="auto, (max-width: 1220px) 100vw, 1220px" /></p>
<h2>8. Erstellen einer virtuellen Umgebung</h2>
<p>Es wird empfohlen, TensorFlow in einer virtuellen Umgebung zu installieren, um Konflikte mit anderen Python-Paketen zu vermeiden. Idealerweise mittels Anaconda. Einfach hier downloaden und installieren: <a href="https://www.anaconda.com/download">https://www.anaconda.com/download</a></p>
<p>Anschließend den Anaconda Navigator starten. Unter Environments &gt; Create  neue Umgebung erstellen. Danach wieder auf Home klicken und eine vorhandene Python IDE von hier aus launchen. Es sollten auch zusätzliche IDEs wie PyCharm nach dem Download automatisch hier angezeigt werden.</p>
<h2>9. Verifizieren der Tensorflow GPU Unterstützung</h2>
<p>Um zu überprüfen, ob die TensorFlow GPU Unterstützung erfolgreich erkannt wurde, sollte zunächst in der Python IDE das Tensorflow Package installiert werden. Aber dabei beachten unter Windows wird die GPU nur bis Version 2.10 erkannt! In den nachfolgenden Versionen nicht mehr! Es muss also Tensorflow 2.10 installiert werden.</p>
<p>Anschließend folgenden Code laufen lassen um eine liste der verfügbaren GPUs anzuzeigen:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python">import tensorflow as tf

tf.config.list_physical_devices('GPU')</pre>
<p>&nbsp;</p>
<p>The post <a href="https://nerd-corner.com/de/tensorflow-gpu-aktivieren-unter-windows/">Tensorflow GPU aktivieren unter Windows</a> appeared first on <a href="https://nerd-corner.com/de">Nerd Corner</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://nerd-corner.com/de/tensorflow-gpu-aktivieren-unter-windows/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
	</channel>
</rss>
