Uygulama Dağıtımına Giriş

Uygulama dağıtımı (Application Deployment), geliştirdiğiniz masaüstü uygulamasını son kullanıcılara ulaştırma sürecidir. Bu süreç, uygulamanızın farklı işletim sistemlerinde çalışabilir hale getirilmesi, gerekli bağımlılıkların paketlenmesi ve kullanıcı dostu kurulum deneyimi sağlanmasını içerir.
Yapay zeka destekli masaüstü uygulamaları için dağıtım süreci, geleneksel uygulamalara göre bazı ek zorluklar içerir:
Büyük Model Dosyaları: AI modelleri genellikle büyük boyutlarda olur (MB'dan GB'lara kadar)
Bağımlılık Karmaşıklığı: TensorFlow, PyTorch, OpenCV gibi kütüphaneler çok sayıda alt bağımlılığa sahiptir
Platform Uyumluluğu: Farklı işletim sistemlerinde farklı optimizasyonlar gerekebilir
Performans Gereksinimleri: GPU desteği, CUDA sürücüleri gibi özel gereksinimler

Dağıtım Stratejileri

1.Standalone Executable: Tüm bağımlılıkları içeren tek dosya
2.Installer Package: Kurulum sihirbazı ile dağıtım
3.Portable Application: Kurulum gerektirmeyen taşınabilir uygulama
4.Container-based: Docker gibi konteyner teknolojileri
5.Cloud-based: Hibrit yaklaşım (UI yerel, AI işleme bulutta)

Python Uygulamalarını Paketleme

PyInstaller ile Executable Oluşturma

PyInstaller, Python uygulamalarını standalone executable dosyalara dönüştüren popüler bir araçtır.
# PyInstaller kurulumu pip install pyinstaller

Basit Uygulama Örneği

# main.py import tkinter as tk from tkinter import messagebox, filedialog import numpy as np import cv2 from PIL import Image, ImageTk import joblib import os class AIImageClassifier: def __init__(self, root): self.root = root self.root.title("AI Görüntü Sınıflandırıcı v1.0") self.root.geometry("600x500") # Model yolunu belirleme if getattr(sys, 'frozen', False): # PyInstaller ile paketlenmiş uygulama self.base_path = sys._MEIPASS else: # Normal Python çalıştırma self.base_path = os.path.dirname(os.path.abspath(__file__)) self.model_path = os.path.join(self.base_path, 'models', 'classifier.pkl') self.load_model() self.setup_ui() def load_model(self): try: self.model = joblib.load(self.model_path) self.model_loaded = True except Exception as e: messagebox.showerror("Model Hatası", f"Model yüklenemedi: {str(e)}") self.model_loaded = False def setup_ui(self): # UI bileşenleri title_label = tk.Label(self.root, text="AI Görüntü Sınıflandırıcı", font=("Arial", 16, "bold")) title_label.pack(pady=10) # Görüntü seçme butonu select_btn = tk.Button(self.root, text="Görüntü Seç", command=self.select_image, font=("Arial", 12)) select_btn.pack(pady=10) # Görüntü gösterme alanı self.image_label = tk.Label(self.root, text="Görüntü seçilmedi", width=40, height=15, bg="lightgray") self.image_label.pack(pady=10) # Tahmin butonu self.predict_btn = tk.Button(self.root, text="Sınıflandır", command=self.classify_image, font=("Arial", 12), state="disabled") self.predict_btn.pack(pady=10) # Sonuç etiketi self.result_label = tk.Label(self.root, text="", font=("Arial", 14, "bold")) self.result_label.pack(pady=10) # Durum çubuğu status_frame = tk.Frame(self.root) status_frame.pack(side=tk.BOTTOM, fill=tk.X) model_status = "Model Yüklendi" if self.model_loaded else "Model Yüklenemedi" self.status_label = tk.Label(status_frame, text=f"Durum: {model_status}", relief=tk.SUNKEN, anchor=tk.W) self.status_label.pack(side=tk.LEFT, fill=tk.X, expand=True) self.selected_image_path = None def select_image(self): file_path = filedialog.askopenfilename( title="Görüntü Seç", filetypes=[("Görüntü dosyaları", "*.png *.jpg *.jpeg *.bmp *.gif")] ) if file_path: self.selected_image_path = file_path self.display_image(file_path) if self.model_loaded: self.predict_btn.config(state="normal") def display_image(self, image_path): try: image = Image.open(image_path) image = image.resize((200, 200), Image.Resampling.LANCZOS) photo = ImageTk.PhotoImage(image) self.image_label.config(image=photo, text="") self.image_label.image = photo except Exception as e: messagebox.showerror("Hata", f"Görüntü yüklenemedi: {str(e)}") def classify_image(self): if not self.selected_image_path or not self.model_loaded: return try: # Görüntüyü işleme (örnek) image = cv2.imread(self.selected_image_path) image = cv2.resize(image, (64, 64)) image_array = image.flatten().reshape(1, -1) / 255.0 # Tahmin yapma prediction = self.model.predict(image_array)[0] probability = self.model.predict_proba(image_array).max() # Sonucu gösterme result_text = f"Sınıf: {prediction}\nGüven: {probability*100:.1f}%" self.result_label.config(text=result_text) except Exception as e: messagebox.showerror("Hata", f"Sınıflandırma hatası: {str(e)}") if __name__ == "__main__": import sys root = tk.Tk() app = AIImageClassifier(root) root.mainloop()

PyInstaller Spec Dosyası

Karmaşık uygulamalar için spec dosyası kullanmak daha iyidir:
# ai_classifier.spec # -*- mode: python ; coding: utf-8 -*- block_cipher = None a = Analysis( ['main.py'], pathex=[], binaries=[], datas=[ ('models/*.pkl', 'models'), # Model dosyalarını dahil et ('assets/*.png', 'assets'), # Görsel dosyaları dahil et ('config.json', '.'), # Konfigürasyon dosyası ], hiddenimports=[ 'sklearn.ensemble._forest', 'sklearn.tree._tree', 'sklearn.neighbors._typedefs', 'sklearn.neighbors._quad_tree', 'sklearn.utils._cython_blas', 'cv2', 'numpy', 'PIL', ], hookspath=[], hooksconfig={}, runtime_hooks=[], excludes=[ 'matplotlib', # Gereksiz kütüphaneleri hariç tut 'scipy.spatial.cKDTree', ], win_no_prefer_redirects=False, win_private_assemblies=False, cipher=block_cipher, noarchive=False, ) pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) exe = EXE( pyz, a.scripts, a.binaries, a.zipfiles, a.datas, [], name='AI_Image_Classifier', debug=False, bootloader_ignore_signals=False, strip=False, upx=True, # UPX sıkıştırma (isteğe bağlı) upx_exclude=[], runtime_tmpdir=None, console=False, # GUI uygulaması için False disable_windowed_traceback=False, target_arch=None, codesign_identity=None, entitlements_file=None, icon='assets/app_icon.ico', # Uygulama ikonu )

Paketleme Komutları

# Basit paketleme pyinstaller --onefile --windowed main.py # Spec dosyası ile paketleme pyinstaller ai_classifier.spec # Gelişmiş seçenekler pyinstaller --onefile \ --windowed \ --icon=app_icon.ico \ --add-data "models;models" \ --add-data "assets;assets" \ --hidden-import=sklearn.ensemble._forest \ --exclude-module=matplotlib \ --name="AI_Image_Classifier" \ main.py

cx_Freeze ile Alternatif Paketleme

# setup.py from cx_Freeze import setup, Executable import sys # Bağımlılıklar build_exe_options = { "packages": ["tkinter", "numpy", "cv2", "PIL", "sklearn", "joblib"], "excludes": ["matplotlib", "scipy"], "include_files": [ ("models/", "models/"), ("assets/", "assets/"), ("config.json", "config.json") ], "zip_include_packages": ["encodings", "PySide2"], } # Platform-specific seçenekler if sys.platform == "win32": base = "Win32GUI" # GUI uygulaması için else: base = None setup( name="AI Image Classifier", version="1.0", description="AI-powered image classification tool", options={"build_exe": build_exe_options}, executables=[ Executable( "main.py", base=base, icon="assets/app_icon.ico", target_name="AI_Image_Classifier" ) ] )
# cx_Freeze ile paketleme python setup.py build

Platform-Specific Dağıtım

Windows için MSI Installer

# installer_setup.py from cx_Freeze import setup, Executable import sys # MSI seçenekleri bdist_msi_options = { "upgrade_code": "{12345678-1234-1234-1234-123456789012}", "add_to_path": False, "initial_target_dir": r"[ProgramFilesFolder]\AI Image Classifier", "install_icon": "assets/app_icon.ico", } setup( name="AI Image Classifier", version="1.0.0", description="AI-powered image classification desktop application", author="Your Name", author_email="your.email@example.com", options={ "build_exe": build_exe_options, "bdist_msi": bdist_msi_options, }, executables=[ Executable( "main.py", base="Win32GUI", icon="assets/app_icon.ico", target_name="AI_Image_Classifier", shortcut_name="AI Image Classifier", shortcut_dir="DesktopFolder", ) ] )
# MSI installer oluşturma python installer_setup.py bdist_msi

macOS için App Bundle

# setup_mac.py from setuptools import setup APP = ['main.py'] DATA_FILES = [ ('models', ['models/classifier.pkl']), ('assets', ['assets/app_icon.icns']), ] OPTIONS = { 'argv_emulation': True, 'iconfile': 'assets/app_icon.icns', 'plist': { 'CFBundleName': 'AI Image Classifier', 'CFBundleDisplayName': 'AI Image Classifier', 'CFBundleGetInfoString': "AI-powered image classification tool", 'CFBundleIdentifier': 'com.yourcompany.aiclassifier', 'CFBundleVersion': '1.0.0', 'CFBundleShortVersionString': '1.0.0', 'NSHumanReadableCopyright': 'Copyright © 2024 Your Company', 'NSHighResolutionCapable': True, }, 'packages': ['tkinter', 'numpy', 'cv2', 'PIL', 'sklearn'], } setup( app=APP, data_files=DATA_FILES, options={'py2app': OPTIONS}, setup_requires=['py2app'], )
# macOS App Bundle oluşturma python setup_mac.py py2app

Linux için AppImage

# AppImage oluşturma scripti #!/bin/bash # Gerekli araçları yükleme wget https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage chmod +x appimagetool-x86_64.AppImage # Uygulama dizini oluşturma mkdir -p AI_Image_Classifier.AppDir/usr/bin mkdir -p AI_Image_Classifier.AppDir/usr/lib mkdir -p AI_Image_Classifier.AppDir/usr/share/applications mkdir -p AI_Image_Classifier.AppDir/usr/share/icons/hicolor/256x256/apps # Executable'ı kopyalama cp dist/AI_Image_Classifier AI_Image_Classifier.AppDir/usr/bin/ # Desktop dosyası oluşturma cat > AI_Image_Classifier.AppDir/AI_Image_Classifier.desktop << EOF [Desktop Entry] Type=Application Name=AI Image Classifier Exec=AI_Image_Classifier Icon=ai_classifier Categories=Graphics;Photography; EOF # AppRun scripti cat > AI_Image_Classifier.AppDir/AppRun << 'EOF' #!/bin/bash HERE="$(dirname "$(readlink -f "${0}" )")" export PATH="${HERE}/usr/bin:${PATH}" export LD_LIBRARY_PATH="${HERE}/usr/lib:${LD_LIBRARY_PATH}" exec "${HERE}/usr/bin/AI_Image_Classifier" "$@" EOF chmod +x AI_Image_Classifier.AppDir/AppRun # İkon kopyalama cp assets/app_icon.png AI_Image_Classifier.AppDir/usr/share/icons/hicolor/256x256/apps/ai_classifier.png cp assets/app_icon.png AI_Image_Classifier.AppDir/ai_classifier.png # AppImage oluşturma ./appimagetool-x86_64.AppImage AI_Image_Classifier.AppDir

Docker ile Konteynerleştirme

# Dockerfile FROM python:3.9-slim # Sistem bağımlılıklarını yükleme RUN apt-get update && apt-get install -y \ libgl1-mesa-glx \ libglib2.0-0 \ libsm6 \ libxext6 \ libxrender-dev \ libgomp1 \ libgthread-2.0-0 \ && rm -rf /var/lib/apt/lists/* # Çalışma dizini WORKDIR /app # Python bağımlılıklarını yükleme COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # Uygulama dosyalarını kopyalama COPY . . # X11 forwarding için gerekli ENV DISPLAY=:0 # Uygulamayı çalıştırma CMD ["python", "main.py"]
# docker-compose.yml version: '3.8' services: ai-classifier: build: . volumes: - /tmp/.X11-unix:/tmp/.X11-unix:rw - ./data:/app/data environment: - DISPLAY=${DISPLAY} network_mode: host stdin_open: true tty: true

Model Optimizasyonu ve Boyut Azaltma

Model Quantization

# model_optimization.py import tensorflow as tf import numpy as np def quantize_model(model_path, output_path): """TensorFlow modelini quantize etme""" # Modeli yükleme model = tf.keras.models.load_model(model_path) # TensorFlow Lite converter converter = tf.lite.TFLiteConverter.from_keras_model(model) # Quantization ayarları converter.optimizations = [tf.lite.Optimize.DEFAULT] converter.target_spec.supported_types = [tf.float16] # Representative dataset (isteğe bağlı) def representative_dataset(): for _ in range(100): yield [np.random.random((1, 224, 224, 3)).astype(np.float32)] converter.representative_dataset = representative_dataset converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8] converter.inference_input_type = tf.uint8 converter.inference_output_type = tf.uint8 # Quantized model oluşturma quantized_model = converter.convert() # Kaydetme with open(output_path, 'wb') as f: f.write(quantized_model) print(f"Quantized model saved to {output_path}") # Boyut karşılaştırması import os original_size = os.path.getsize(model_path) quantized_size = os.path.getsize(output_path) print(f"Original size: {original_size / 1024 / 1024:.2f} MB") print(f"Quantized size: {quantized_size / 1024 / 1024:.2f} MB") print(f"Size reduction: {(1 - quantized_size/original_size)*100:.1f}%") # Kullanım # quantize_model('models/large_model.h5', 'models/quantized_model.tflite')

Model Pruning

# model_pruning.py import tensorflow as tf import tensorflow_model_optimization as tfmot def prune_model(model, target_sparsity=0.5): """Model pruning ile boyut azaltma""" # Pruning parametreleri pruning_params = { 'pruning_schedule': tfmot.sparsity.keras.PolynomialDecay( initial_sparsity=0.0, final_sparsity=target_sparsity, begin_step=0, end_step=1000 ) } # Modeli prune etme model_for_pruning = tfmot.sparsity.keras.prune_low_magnitude( model, **pruning_params ) # Modeli derleme model_for_pruning.compile( optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'] ) return model_for_pruning def strip_pruning(model): """Pruning wrapper'ını kaldırma""" return tfmot.sparsity.keras.strip_pruning(model)

Güvenlik ve Lisanslama

Kod Obfuscation

# obfuscation_example.py import base64 import zlib def obfuscate_code(source_code): """Basit kod gizleme""" # Kodu sıkıştır ve base64 encode et compressed = zlib.compress(source_code.encode()) encoded = base64.b64encode(compressed).decode() # Obfuscated kod şablonu obfuscated = f""" import base64 import zlib exec(zlib.decompress(base64.b64decode('{encoded}')).decode()) """ return obfuscated # Kullanım # with open('sensitive_module.py', 'r') as f: # source = f.read() # # obfuscated = obfuscate_code(source) # with open('obfuscated_module.py', 'w') as f: # f.write(obfuscated)

Lisans Kontrolü

# license_manager.py import hashlib import datetime import json from cryptography.fernet import Fernet class LicenseManager: def __init__(self, key=None): if key is None: key = Fernet.generate_key() self.cipher = Fernet(key) self.key = key def generate_license(self, user_info, expiry_date, features=None): """Lisans oluşturma""" license_data = { 'user': user_info, 'expiry': expiry_date.isoformat(), 'features': features or [], 'generated': datetime.datetime.now().isoformat() } # JSON'u şifrele json_data = json.dumps(license_data).encode() encrypted = self.cipher.encrypt(json_data) return encrypted.hex() def validate_license(self, license_key): """Lisans doğrulama""" try: # Hex'den bytes'a çevir encrypted_data = bytes.fromhex(license_key) # Şifreyi çöz decrypted = self.cipher.decrypt(encrypted_data) license_data = json.loads(decrypted.decode()) # Tarih kontrolü expiry_date = datetime.datetime.fromisoformat(license_data['expiry']) if datetime.datetime.now() > expiry_date: return False, "License expired" return True, license_data except Exception as e: return False, f"Invalid license: {str(e)}" def check_feature(self, license_key, feature): """Özellik kontrolü""" valid, data = self.validate_license(license_key) if not valid: return False if isinstance(data, dict): return feature in data.get('features', []) return False # Kullanım örneği def license_protected_app(): license_manager = LicenseManager() # Uygulama başlangıcında lisans kontrolü license_file = "license.key" try: with open(license_file, 'r') as f: license_key = f.read().strip() valid, info = license_manager.validate_license(license_key) if not valid: print(f"License error: {info}") return False print(f"License valid for: {info['user']}") return True except FileNotFoundError: print("License file not found!") return False

Otomatik Güncelleme Sistemi

# auto_updater.py import requests import json import os import subprocess import tempfile from packaging import version class AutoUpdater: def __init__(self, current_version, update_url): self.current_version = current_version self.update_url = update_url def check_for_updates(self): """Güncelleme kontrolü""" try: response = requests.get(f"{self.update_url}/version.json", timeout=10) response.raise_for_status() update_info = response.json() latest_version = update_info['version'] if version.parse(latest_version) > version.parse(self.current_version): return True, update_info else: return False, None except Exception as e: print(f"Update check failed: {e}") return False, None def download_update(self, update_info): """Güncellemeyi indirme""" download_url = update_info['download_url'] try: response = requests.get(download_url, stream=True, timeout=30) response.raise_for_status() # Geçici dosyaya indirme with tempfile.NamedTemporaryFile(delete=False, suffix='.exe') as tmp_file: for chunk in response.iter_content(chunk_size=8192): tmp_file.write(chunk) return tmp_file.name except Exception as e: print(f"Download failed: {e}") return None def install_update(self, installer_path): """Güncellemeyi yükleme""" try: # Installer'ı çalıştır subprocess.Popen([installer_path, '/S']) # Silent install # Mevcut uygulamayı kapat os._exit(0) except Exception as e: print(f"Installation failed: {e}") return False def auto_update_check(self): """Otomatik güncelleme kontrolü ve kurulumu""" has_update, update_info = self.check_for_updates() if has_update: print(f"New version available: {update_info['version']}") # Kullanıcıya sor (GUI'de dialog göster) user_choice = input("Do you want to update now? (y/n): ") if user_choice.lower() == 'y': installer_path = self.download_update(update_info) if installer_path: self.install_update(installer_path) return True return False # Uygulama başlangıcında kullanım def main(): updater = AutoUpdater("1.0.0", "https://your-server.com/updates" ) # Güncelleme kontrolü (arka planda) import threading update_thread = threading.Thread(target=updater.auto_update_check) update_thread.daemon = True update_thread.start() # Ana uygulama kodunuz buraya gelir # ...

Dağıtım Checklist

Ön Dağıtım Kontrolleri

# deployment_checklist.py import os import sys import subprocess import json class DeploymentChecker: def __init__(self): self.checks = [] self.errors = [] self.warnings = [] def check_dependencies(self): """Bağımlılık kontrolü""" required_packages = [ 'numpy', 'opencv-python', 'scikit-learn', 'tensorflow', 'pillow', 'joblib' ] missing_packages = [] for package in required_packages: try: __import__(package.replace('-', '_')) except ImportError: missing_packages.append(package) if missing_packages: self.errors.append(f"Missing packages: {missing_packages}") else: self.checks.append("✓ All dependencies available") def check_model_files(self): """Model dosyası kontrolü""" model_paths = [ 'models/classifier.pkl', 'models/preprocessor.pkl' ] missing_models = [] for path in model_paths: if not os.path.exists(path): missing_models.append(path) if missing_models: self.errors.append(f"Missing model files: {missing_models}") else: self.checks.append("✓ All model files present") def check_file_sizes(self): """Dosya boyutu kontrolü""" large_files = [] for root, dirs, files in os.walk('.'): for file in files: file_path = os.path.join(root, file) size_mb = os.path.getsize(file_path) / (1024 * 1024) if size_mb > 100: # 100MB'dan büyük dosyalar large_files.append((file_path, f"{size_mb:.1f}MB")) if large_files: self.warnings.append(f"Large files detected: {large_files}") else: self.checks.append("✓ No unusually large files") def check_permissions(self): """İzin kontrolü""" critical_files = ['main.py', 'models/', 'assets/'] permission_issues = [] for item in critical_files: if os.path.exists(item): if not os.access(item, os.R_OK): permission_issues.append(f"No read access: {item}") else: permission_issues.append(f"File not found: {item}") if permission_issues: self.errors.append(f"Permission issues: {permission_issues}") else: self.checks.append("✓ File permissions OK") def run_all_checks(self): """Tüm kontrolleri çalıştır""" print("Running deployment checks...\n") self.check_dependencies() self.check_model_files() self.check_file_sizes() self.check_permissions() # Sonuçları göster print("✅ PASSED CHECKS:") for check in self.checks: print(f" {check}") if self.warnings: print("\n⚠️ WARNINGS:") for warning in self.warnings: print(f" {warning}") if self.errors: print("\n❌ ERRORS:") for error in self.errors: print(f" {error}") print("\n🚫 Deployment not recommended!") return False else: print("\n✅ Ready for deployment!") return True # Kullanım if __name__ == "__main__": checker = DeploymentChecker() ready = checker.run_all_checks() sys.exit(0 if ready else 1)

Sonuç

Bu derste, yapay zeka destekli masaüstü uygulamalarınızı son kullanıcılara nasıl dağıtacağınızı öğrendiniz. Dağıtım süreci, uygulamanızın başarısı için kritik öneme sahiptir. Doğru paketleme, optimizasyon ve güvenlik önlemleri alarak, kullanıcılarınıza profesyonel bir deneyim sunabilirsiniz.
Önemli noktalar:
Model boyutlarını optimize edin
Platform-specific paketleme araçlarını kullanın
Güvenlik ve lisanslama konularını ihmal etmeyin
Otomatik güncelleme sistemi kurun
Dağıtım öncesi kapsamlı testler yapın
Bu eğitim serisini tamamladığınızda, yapay zeka teknolojilerini masaüstü uygulamalarına entegre etme konusunda sağlam bir temel kazanmış olacaksınız.