feat: improved questions
1
.github/agents/obrona-expander.agent.md
vendored
@ -181,6 +181,7 @@ Start with the most basic concepts, then build on them. Explain "graf" before "a
|
||||
- No fenced code blocks (4-space indented blocks only)
|
||||
- Polish language throughout (English in parentheses for standard terms)
|
||||
- Terms are ordered from foundational to advanced
|
||||
- Images are NOT ASCII, images should be actual images, monchrome black and white laser printer a4 friendly
|
||||
|
||||
### BATCH PROCESSING
|
||||
|
||||
|
||||
381
pytania/generate_bf_negative_diagram.py
Normal file
@ -0,0 +1,381 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Generate Bellman-Ford negative-weights & negative-cycle diagram for PYTANIE 2.
|
||||
|
||||
Two-part figure:
|
||||
Part 1: Graph with negative edge, Dijkstra WRONG vs Bellman-Ford CORRECT
|
||||
Part 2: Negative cycle detection (add C→B(−3))
|
||||
|
||||
A4-compatible, monochrome-friendly, 300 DPI.
|
||||
"""
|
||||
|
||||
import matplotlib
|
||||
matplotlib.use('Agg')
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.patches as mpatches
|
||||
import numpy as np
|
||||
import os
|
||||
|
||||
DPI = 300
|
||||
BG = 'white'
|
||||
LN = 'black'
|
||||
FS = 8
|
||||
FS_TITLE = 10
|
||||
FS_SMALL = 6.5
|
||||
FS_EDGE = 9
|
||||
OUTPUT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'img')
|
||||
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
||||
|
||||
GRAY1 = '#E8E8E8'
|
||||
GRAY2 = '#D0D0D0'
|
||||
GRAY3 = '#B8B8B8'
|
||||
GRAY4 = '#F5F5F5'
|
||||
LIGHT_GREEN = '#D5E8D4'
|
||||
LIGHT_RED = '#F8D7DA'
|
||||
LIGHT_YELLOW = '#FFF9C4'
|
||||
|
||||
# --- Graph layout for negative-weight example ---
|
||||
# S→A(2), A→C(3), S→B(5), B→A(−4)
|
||||
NEG_POS = {'S': (0.8, 2), 'A': (3.3, 3.2), 'B': (3.3, 0.8), 'C': (5.8, 2)}
|
||||
NEG_EDGES = [('S', 'A', 2), ('A', 'C', 3), ('S', 'B', 5), ('B', 'A', -4)]
|
||||
|
||||
|
||||
def draw_node(ax, name, pos, color='white', current=False, visited=False,
|
||||
dist_label=None, fontsize=12, error=False):
|
||||
x, y = pos
|
||||
r = 0.35
|
||||
lw = 2.5 if current else 1.5
|
||||
ec = '#D32F2F' if current else ('#D32F2F' if error else LN)
|
||||
fc = LIGHT_YELLOW if current else (LIGHT_GREEN if visited else color)
|
||||
if error:
|
||||
fc = LIGHT_RED
|
||||
|
||||
circle = plt.Circle((x, y), r, fill=True, facecolor=fc,
|
||||
edgecolor=ec, linewidth=lw, zorder=5)
|
||||
ax.add_patch(circle)
|
||||
ax.text(x, y, name, ha='center', va='center', fontsize=fontsize,
|
||||
fontweight='bold', zorder=6)
|
||||
|
||||
if dist_label is not None:
|
||||
bbox_ec = '#D32F2F' if error else GRAY3
|
||||
bbox_fc = LIGHT_RED if error else 'white'
|
||||
ax.text(x, y - 0.55, f'd={dist_label}', ha='center', va='center',
|
||||
fontsize=FS, zorder=6,
|
||||
bbox=dict(boxstyle='round,pad=0.15', facecolor=bbox_fc,
|
||||
edgecolor=bbox_ec, alpha=0.95))
|
||||
|
||||
|
||||
def draw_edge(ax, pos1, pos2, weight, highlighted=False, relaxed=False,
|
||||
negative=False, cycle_edge=False, offset=0.0):
|
||||
x1, y1 = pos1
|
||||
x2, y2 = pos2
|
||||
|
||||
dx, dy = x2 - x1, y2 - y1
|
||||
length = np.sqrt(dx**2 + dy**2)
|
||||
r = 0.38
|
||||
sx = x1 + r * dx / length
|
||||
sy = y1 + r * dy / length
|
||||
ex = x2 - r * dx / length
|
||||
ey = y2 - r * dy / length
|
||||
|
||||
# Offset perpendicular for parallel edges
|
||||
if offset != 0:
|
||||
perp_x = -dy / length * offset
|
||||
perp_y = dx / length * offset
|
||||
sx += perp_x; sy += perp_y
|
||||
ex += perp_x; ey += perp_y
|
||||
|
||||
if cycle_edge:
|
||||
color = '#D32F2F'
|
||||
lw = 2.5
|
||||
ls = '--'
|
||||
elif negative:
|
||||
color = '#D32F2F'
|
||||
lw = 2.5
|
||||
ls = '-'
|
||||
elif relaxed:
|
||||
color = '#D32F2F'
|
||||
lw = 2.5
|
||||
ls = '-'
|
||||
elif highlighted:
|
||||
color = '#1565C0'
|
||||
lw = 2.0
|
||||
ls = '-'
|
||||
else:
|
||||
color = GRAY3
|
||||
lw = 1.5
|
||||
ls = '-'
|
||||
|
||||
# Arrow
|
||||
ax.annotate('', xy=(ex, ey), xytext=(sx, sy),
|
||||
arrowprops=dict(arrowstyle='->', color=color, lw=lw,
|
||||
linestyle=ls, shrinkA=0, shrinkB=0),
|
||||
zorder=2)
|
||||
|
||||
# Weight label
|
||||
mx = (sx + ex) / 2
|
||||
my = (sy + ey) / 2
|
||||
perp_x = -dy / length * 0.22
|
||||
perp_y = dx / length * 0.22
|
||||
if offset != 0:
|
||||
perp_x *= 0.5
|
||||
perp_y *= 0.5
|
||||
|
||||
weight_str = str(weight)
|
||||
edge_fc = LIGHT_RED if negative or cycle_edge else 'white'
|
||||
edge_ec = '#D32F2F' if negative or cycle_edge else GRAY3
|
||||
ax.text(mx + perp_x, my + perp_y, weight_str, ha='center', va='center',
|
||||
fontsize=FS_EDGE, fontweight='bold',
|
||||
bbox=dict(boxstyle='round,pad=0.15', facecolor=edge_fc,
|
||||
edgecolor=edge_ec, alpha=0.95),
|
||||
zorder=4)
|
||||
|
||||
|
||||
def draw_neg_graph(ax, edges, title="", dist=None, current=None, visited=None,
|
||||
relaxed_edges=None, error_nodes=None, extra_edges=None,
|
||||
node_positions=None):
|
||||
if visited is None:
|
||||
visited = set()
|
||||
if relaxed_edges is None:
|
||||
relaxed_edges = set()
|
||||
if dist is None:
|
||||
dist = {}
|
||||
if error_nodes is None:
|
||||
error_nodes = set()
|
||||
if node_positions is None:
|
||||
node_positions = NEG_POS
|
||||
|
||||
ax.set_xlim(-0.5, 7.0)
|
||||
ax.set_ylim(-0.8, 4.5)
|
||||
ax.set_aspect('equal')
|
||||
ax.axis('off')
|
||||
if title:
|
||||
ax.set_title(title, fontsize=FS, fontweight='bold', pad=5)
|
||||
|
||||
all_edges = list(edges)
|
||||
if extra_edges:
|
||||
all_edges += extra_edges
|
||||
|
||||
for u, v, w in all_edges:
|
||||
rl = (u, v) in relaxed_edges
|
||||
neg = w < 0
|
||||
cycle = extra_edges and (u, v, w) in extra_edges
|
||||
# If B→A and A→B both exist, offset them
|
||||
off = 0.0
|
||||
draw_edge(ax, node_positions[u], node_positions[v], w,
|
||||
relaxed=rl, negative=neg, cycle_edge=cycle, offset=off)
|
||||
|
||||
for name, pos in node_positions.items():
|
||||
is_current = (name == current)
|
||||
is_visited = (name in visited)
|
||||
d_label = dist.get(name, None)
|
||||
is_error = (name in error_nodes)
|
||||
draw_node(ax, name, pos, current=is_current, visited=is_visited,
|
||||
dist_label=d_label, error=is_error)
|
||||
|
||||
|
||||
def generate_bf_negative_weights():
|
||||
"""
|
||||
Two-row figure:
|
||||
Row 1: Graph structure + Dijkstra WRONG + Bellman-Ford CORRECT
|
||||
Row 2: B-F iterations 1-3 step by step
|
||||
"""
|
||||
fig = plt.figure(figsize=(14, 10))
|
||||
fig.suptitle('Bellman-Ford — ujemne wagi vs Dijkstra\n'
|
||||
'Graf: S→A(2), A→C(3), S→B(5), B→A(−4). Start = S',
|
||||
fontsize=FS_TITLE + 1, fontweight='bold', y=0.99)
|
||||
|
||||
# ---- Row 1: Graph + Dijkstra wrong + BF correct ----
|
||||
|
||||
# Panel 1: The graph structure
|
||||
ax1 = fig.add_subplot(2, 3, 1)
|
||||
draw_neg_graph(ax1, NEG_EDGES,
|
||||
title='Graf z ujemną wagą\n(B→A = −4, zaznaczona na czerwono)',
|
||||
dist={'S': '0', 'A': '?', 'B': '?', 'C': '?'})
|
||||
# START label
|
||||
ax1.annotate("START", xy=(NEG_POS['S'][0] - 0.35, NEG_POS['S'][1]),
|
||||
xytext=(NEG_POS['S'][0] - 1.2, NEG_POS['S'][1]),
|
||||
fontsize=FS, fontweight='bold', color='#D32F2F',
|
||||
arrowprops=dict(arrowstyle='->', color='#D32F2F', lw=2),
|
||||
va='center')
|
||||
|
||||
# Panel 2: Dijkstra — WRONG
|
||||
ax2 = fig.add_subplot(2, 3, 2)
|
||||
draw_neg_graph(ax2, NEG_EDGES,
|
||||
title='Dijkstra — BŁĘDNY wynik\nA zamknięty z d=2, nie poprawia przy B→A',
|
||||
dist={'S': '0', 'A': '2', 'B': '5', 'C': '5'},
|
||||
visited={'S', 'A', 'B', 'C'},
|
||||
error_nodes={'A', 'C'})
|
||||
# Add "WRONG" annotations
|
||||
ax2.text(NEG_POS['A'][0] + 0.6, NEG_POS['A'][1] + 0.3, '✗ powinno 1',
|
||||
fontsize=FS_SMALL, color='#D32F2F', fontweight='bold',
|
||||
bbox=dict(boxstyle='round,pad=0.1', facecolor=LIGHT_RED,
|
||||
edgecolor='#D32F2F', alpha=0.9, lw=0.5))
|
||||
ax2.text(NEG_POS['C'][0] + 0.05, NEG_POS['C'][1] + 0.55, '✗ powinno 4',
|
||||
fontsize=FS_SMALL, color='#D32F2F', fontweight='bold',
|
||||
bbox=dict(boxstyle='round,pad=0.1', facecolor=LIGHT_RED,
|
||||
edgecolor='#D32F2F', alpha=0.9, lw=0.5))
|
||||
|
||||
# Panel 3: Bellman-Ford — CORRECT
|
||||
ax3 = fig.add_subplot(2, 3, 3)
|
||||
draw_neg_graph(ax3, NEG_EDGES,
|
||||
title='Bellman-Ford — POPRAWNY wynik\nUjemna waga B→A poprawnie propagowana',
|
||||
dist={'S': '0', 'A': '1', 'B': '5', 'C': '4'},
|
||||
visited={'S', 'A', 'B', 'C'},
|
||||
relaxed_edges={('B', 'A')})
|
||||
ax3.text(NEG_POS['A'][0] + 0.6, NEG_POS['A'][1] + 0.3, '✓ poprawne!',
|
||||
fontsize=FS_SMALL, color='#006400', fontweight='bold',
|
||||
bbox=dict(boxstyle='round,pad=0.1', facecolor=LIGHT_GREEN,
|
||||
edgecolor='#006400', alpha=0.9, lw=0.5))
|
||||
ax3.text(NEG_POS['C'][0] + 0.05, NEG_POS['C'][1] + 0.55, '✓ poprawne!',
|
||||
fontsize=FS_SMALL, color='#006400', fontweight='bold',
|
||||
bbox=dict(boxstyle='round,pad=0.1', facecolor=LIGHT_GREEN,
|
||||
edgecolor='#006400', alpha=0.9, lw=0.5))
|
||||
|
||||
# ---- Row 2: B-F iterations step by step ----
|
||||
iterations = [
|
||||
{
|
||||
'title': 'B-F Iteracja 1\nRelaksuj WSZYSTKIE krawędzie',
|
||||
'dist': {'S': '0', 'A': '1', 'B': '5', 'C': '5'},
|
||||
'relaxed': {('S', 'A'), ('A', 'C'), ('S', 'B'), ('B', 'A')},
|
||||
'detail': ('S→A: 0+2=2<∞ → A=2\n'
|
||||
'A→C: 2+3=5<∞ → C=5\n'
|
||||
'S→B: 0+5=5<∞ → B=5\n'
|
||||
'B→A: 5−4=1<2 → A=1 ✓'),
|
||||
},
|
||||
{
|
||||
'title': 'B-F Iteracja 2\nPropagacja poprawionego A',
|
||||
'dist': {'S': '0', 'A': '1', 'B': '5', 'C': '4'},
|
||||
'relaxed': {('A', 'C')},
|
||||
'detail': ('S→A: 0+2=2>1 ✗\n'
|
||||
'A→C: 1+3=4<5 → C=4 ✓\n'
|
||||
'S→B: 0+5=5=5 ✗\n'
|
||||
'B→A: 5−4=1=1 ✗'),
|
||||
},
|
||||
{
|
||||
'title': 'B-F Iteracja 3\nBrak zmian → stabilne!',
|
||||
'dist': {'S': '0', 'A': '1', 'B': '5', 'C': '4'},
|
||||
'relaxed': set(),
|
||||
'detail': ('Wszystkie krawędzie:\n'
|
||||
'brak poprawy ✗\n'
|
||||
'→ wynik stabilny\n'
|
||||
'→ BRAK cyklu ujemnego'),
|
||||
},
|
||||
]
|
||||
|
||||
for i, it in enumerate(iterations):
|
||||
ax = fig.add_subplot(2, 3, i + 4)
|
||||
draw_neg_graph(ax, NEG_EDGES, title=it['title'],
|
||||
dist=it['dist'],
|
||||
visited={'S', 'A', 'B', 'C'},
|
||||
relaxed_edges=it['relaxed'])
|
||||
# Detail text below graph
|
||||
ax.text(3.2, -0.5, it['detail'], ha='center', va='top',
|
||||
fontsize=FS_SMALL, family='monospace',
|
||||
bbox=dict(boxstyle='round,pad=0.3', facecolor=GRAY4,
|
||||
edgecolor=GRAY3))
|
||||
|
||||
# Bottom note
|
||||
fig.text(0.5, 0.01,
|
||||
'Dijkstra zamyka wierzchołki na stałe (zachłanność) → ujemna waga B→A(−4) nie może poprawić zamkniętego A.\n'
|
||||
'Bellman-Ford relaksuje WSZYSTKIE krawędzie w każdej iteracji → ujemne wagi propagują się poprawnie.',
|
||||
ha='center', fontsize=FS, fontweight='bold',
|
||||
bbox=dict(boxstyle='round,pad=0.3', facecolor=LIGHT_YELLOW, edgecolor=LN))
|
||||
|
||||
plt.tight_layout(rect=[0, 0.05, 1, 0.95])
|
||||
plt.savefig(os.path.join(OUTPUT_DIR, 'bellman_ford_negative_weights.png'),
|
||||
dpi=DPI, bbox_inches='tight', facecolor=BG)
|
||||
plt.close()
|
||||
print(" ✓ bellman_ford_negative_weights.png")
|
||||
|
||||
|
||||
def generate_bf_negative_cycle():
|
||||
"""
|
||||
Figure showing negative cycle detection.
|
||||
Graph: S→A(2), A→C(3), S→B(5), B→A(−4), C→B(−3) [added edge]
|
||||
Cycle: B→A→C→B = −4+3+(−3) = −4 < 0
|
||||
"""
|
||||
cycle_edges = NEG_EDGES + [('C', 'B', -3)]
|
||||
|
||||
fig = plt.figure(figsize=(14, 5.5))
|
||||
fig.suptitle('Bellman-Ford — wykrywanie cyklu ujemnego\n'
|
||||
'Dodano krawędź C→B(−3). Cykl: B→A→C→B = −4+3+(−3) = −4 < 0',
|
||||
fontsize=FS_TITLE + 1, fontweight='bold', y=0.99)
|
||||
|
||||
# Panel 1: Graph with cycle highlighted
|
||||
ax1 = fig.add_subplot(1, 3, 1)
|
||||
draw_neg_graph(ax1, NEG_EDGES,
|
||||
title='Graf z cyklem ujemnym\nDodana krawędź C→B(−3) — przerywana',
|
||||
dist={'S': '0', 'A': '?', 'B': '?', 'C': '?'},
|
||||
extra_edges=[('C', 'B', -3)])
|
||||
# Mark cycle
|
||||
ax1.annotate("CYKL\n−4+3+(−3)=−4<0",
|
||||
xy=(3.3, 2.0),
|
||||
fontsize=FS, fontweight='bold', color='#D32F2F',
|
||||
ha='center', va='center',
|
||||
bbox=dict(boxstyle='round,pad=0.3', facecolor=LIGHT_RED,
|
||||
edgecolor='#D32F2F', alpha=0.9))
|
||||
|
||||
# Panel 2: After V−1 iterations — still changing
|
||||
ax2 = fig.add_subplot(1, 3, 2)
|
||||
draw_neg_graph(ax2, NEG_EDGES,
|
||||
title='Po V−1=3 iteracjach\ndist wciąż maleje (niestabilne!)',
|
||||
dist={'S': '0', 'A': '−7', 'B': '−4', 'C': '−4'},
|
||||
visited={'S', 'A', 'B', 'C'},
|
||||
error_nodes={'A', 'B', 'C'},
|
||||
extra_edges=[('C', 'B', -3)])
|
||||
ax2.text(3.2, -0.4,
|
||||
'Każde okrążenie cyklu\nzmniejsza dist o 4.\n'
|
||||
'Dist → −∞ (brak minimum!)',
|
||||
ha='center', va='top', fontsize=FS_SMALL, fontweight='bold',
|
||||
bbox=dict(boxstyle='round,pad=0.3', facecolor=LIGHT_RED,
|
||||
edgecolor='#D32F2F'))
|
||||
|
||||
# Panel 3: V-th iteration detects
|
||||
ax3 = fig.add_subplot(1, 3, 3)
|
||||
ax3.axis('off')
|
||||
ax3.set_xlim(0, 10)
|
||||
ax3.set_ylim(0, 10)
|
||||
|
||||
detection_text = (
|
||||
"V-ta iteracja (sprawdzenie):\n"
|
||||
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n"
|
||||
"for (src, dst, w) in edges:\n"
|
||||
" if dist[src]+w < dist[dst]:\n"
|
||||
" return None # CYKL!\n\n"
|
||||
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n"
|
||||
"Sprawdzamy np. krawędź B→A:\n"
|
||||
" dist[B] + (−4) = −4 + (−4) = −8\n"
|
||||
" −8 < dist[A] = −7\n"
|
||||
" → NADAL SIĘ POPRAWIA!\n"
|
||||
" → CYKL UJEMNY WYKRYTY!\n\n"
|
||||
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n"
|
||||
"Wynik: return None\n"
|
||||
"(najkrótsza ścieżka nie istnieje)"
|
||||
)
|
||||
ax3.text(5, 5, detection_text, ha='center', va='center',
|
||||
fontsize=FS + 0.5, family='monospace',
|
||||
bbox=dict(boxstyle='round,pad=0.6', facecolor=LIGHT_RED,
|
||||
edgecolor='#D32F2F', lw=2))
|
||||
ax3.set_title('Wykrywanie — V-ta iteracja\nJeśli cokolwiek się poprawia → cykl ujemny!',
|
||||
fontsize=FS, fontweight='bold', pad=5)
|
||||
|
||||
# Bottom note
|
||||
fig.text(0.5, 0.01,
|
||||
'Bez cyklu ujemnego: po V−1 iteracjach dist jest stabilne. '
|
||||
'Z cyklem ujemnym: dist maleje w nieskończoność → V-ta iteracja to wykrywa.',
|
||||
ha='center', fontsize=FS, fontweight='bold',
|
||||
bbox=dict(boxstyle='round,pad=0.3', facecolor=LIGHT_YELLOW, edgecolor=LN))
|
||||
|
||||
plt.tight_layout(rect=[0, 0.06, 1, 0.94])
|
||||
plt.savefig(os.path.join(OUTPUT_DIR, 'bellman_ford_negative_cycle.png'),
|
||||
dpi=DPI, bbox_inches='tight', facecolor=BG)
|
||||
plt.close()
|
||||
print(" ✓ bellman_ford_negative_cycle.png")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("Generating Bellman-Ford negative weight diagrams...")
|
||||
generate_bf_negative_weights()
|
||||
generate_bf_negative_cycle()
|
||||
print(f"\nAll diagrams saved to {OUTPUT_DIR}/")
|
||||
@ -286,6 +286,220 @@ def generate_three_pillars():
|
||||
print(f" Saved: {out}")
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 4. Filled-in Observer Pattern Card
|
||||
# ============================================================
|
||||
def generate_observer_card_filled():
|
||||
fig, ax = plt.subplots(figsize=(8.27, 8.5))
|
||||
ax.set_xlim(0, 10)
|
||||
ax.set_ylim(0, 10)
|
||||
ax.set_aspect('equal')
|
||||
ax.axis('off')
|
||||
fig.patch.set_facecolor(BG)
|
||||
ax.set_title('Wypełniona karta wzorca — Observer (GoF)',
|
||||
fontsize=FS_TITLE, fontweight='bold', pad=15)
|
||||
|
||||
# Main card outline
|
||||
card_x, card_y, card_w, card_h = 0.8, 0.3, 8.4, 9.2
|
||||
card = FancyBboxPatch((card_x, card_y), card_w, card_h,
|
||||
boxstyle="round,pad=0.15", lw=2.5,
|
||||
edgecolor=LN, facecolor=GRAY4)
|
||||
ax.add_patch(card)
|
||||
|
||||
# Fields with actual Observer content
|
||||
fields = [
|
||||
("Na", "NAZWA", "Observer", GRAY2, True),
|
||||
("P", "PROBLEM", "Obiekt (Subject) zmienia stan → wielu zależnych\n"
|
||||
"obiektów musi zareagować, ale Subject nie\n"
|
||||
"powinien znać ich konkretnych typów.", GRAY1, False),
|
||||
("Si", "SIŁY", "• loose coupling (nie znać obserwatorów z nazwy)\n"
|
||||
" vs koszt powiadomień (N obserwatorów = N wywołań)\n"
|
||||
"• otwartość na rozszerzenia vs złożoność debugowania", 'white', False),
|
||||
("Ro", "ROZWIĄZANIE", "Subject przechowuje listę Observer.\n"
|
||||
"Metody: attach(o), detach(o), notify().\n"
|
||||
"notify() iteruje po liście i woła update()\n"
|
||||
"na każdym obserwatorze.", GRAY1, False),
|
||||
("Ko", "KONSEKWENCJE", "(+) Luźne wiązanie — Subject ↔ Observer\n"
|
||||
"(+) Nowi obserwatorzy bez zmian w Subject\n"
|
||||
"(−) Kaskada powiadomień może być kosztowna\n"
|
||||
"(−) Memory leaks jeśli nie detach()", 'white', False),
|
||||
]
|
||||
|
||||
band_x = card_x + 0.3
|
||||
band_w = card_w - 0.6
|
||||
start_y = card_y + card_h - 0.65
|
||||
|
||||
for i, (abbr, title, content, fill, is_title_field) in enumerate(fields):
|
||||
if is_title_field:
|
||||
band_h = 0.7
|
||||
elif i == 1:
|
||||
band_h = 1.3
|
||||
elif i == 2:
|
||||
band_h = 1.4
|
||||
elif i == 3:
|
||||
band_h = 1.5
|
||||
else:
|
||||
band_h = 1.5
|
||||
|
||||
by = start_y - sum(
|
||||
(0.7 if j == 0 else 1.3 if j == 1 else 1.4 if j == 2 else 1.5 if j == 3 else 1.5) + 0.15
|
||||
for j in range(i)
|
||||
)
|
||||
|
||||
# Abbreviation circle
|
||||
circle = plt.Circle((band_x + 0.35, by + band_h / 2), 0.28,
|
||||
lw=1.5, edgecolor=LN, facecolor=GRAY3)
|
||||
ax.add_patch(circle)
|
||||
ax.text(band_x + 0.35, by + band_h / 2, abbr, ha='center', va='center',
|
||||
fontsize=10, fontweight='bold')
|
||||
|
||||
# Field box
|
||||
fx = band_x + 0.8
|
||||
fw = band_w - 0.8
|
||||
rect = FancyBboxPatch((fx, by), fw, band_h,
|
||||
boxstyle="round,pad=0.06", lw=1,
|
||||
edgecolor=LN, facecolor=fill)
|
||||
ax.add_patch(rect)
|
||||
|
||||
if is_title_field:
|
||||
ax.text(fx + fw / 2, by + band_h / 2, f"{title}: {content}",
|
||||
ha='center', va='center', fontsize=12, fontweight='bold')
|
||||
else:
|
||||
ax.text(fx + 0.15, by + band_h - 0.2, title, ha='left', va='center',
|
||||
fontsize=FS, fontweight='bold')
|
||||
ax.text(fx + 0.15, by + band_h / 2 - 0.15, content, ha='left', va='center',
|
||||
fontsize=FS_SMALL, family='monospace', linespacing=1.3)
|
||||
|
||||
# Arrow
|
||||
if i < len(fields) - 1:
|
||||
draw_arrow(ax, band_x + 0.35, by - 0.02,
|
||||
band_x + 0.35, by - 0.13, lw=1.0)
|
||||
|
||||
# Extra info at bottom
|
||||
extra_y = 0.55
|
||||
extras = [
|
||||
"Powiązane: Mediator (centralizuje), Pub/Sub (rozproszony), MVC (View = Observer)",
|
||||
"Znane użycia: Java Swing listeners, C# event/delegate, React useState, DOM addEventListener"
|
||||
]
|
||||
for j, txt in enumerate(extras):
|
||||
ax.text(card_x + card_w / 2, extra_y + (1 - j) * 0.25, txt,
|
||||
ha='center', va='center', fontsize=FS_SMALL, fontstyle='italic',
|
||||
color='#444444')
|
||||
|
||||
fig.tight_layout()
|
||||
out = os.path.join(OUTPUT_DIR, 'q14_observer_card_filled.png')
|
||||
fig.savefig(out, dpi=DPI, bbox_inches='tight', facecolor=BG)
|
||||
plt.close(fig)
|
||||
print(f" Saved: {out}")
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 5. Pattern Language Navigation Graph
|
||||
# ============================================================
|
||||
def generate_pattern_language_navigation():
|
||||
fig, ax = plt.subplots(figsize=(8.27, 9))
|
||||
ax.set_xlim(0, 12)
|
||||
ax.set_ylim(0, 12)
|
||||
ax.set_aspect('equal')
|
||||
ax.axis('off')
|
||||
fig.patch.set_facecolor(BG)
|
||||
ax.set_title('Język wzorców — nawigacja „problem → wzorzec → nowy problem"',
|
||||
fontsize=FS_TITLE, fontweight='bold', pad=15)
|
||||
|
||||
# Node positions: (x, y, label, is_pattern, fill)
|
||||
# Left column: problems; Right column: patterns
|
||||
nodes = [
|
||||
# Problems (left, rounded rect, white)
|
||||
(1.5, 10.5, "Monolith\nnie skaluje się", False, 'white'),
|
||||
(1.5, 8.2, "Jak routować\nżądania do\nserwisów?", False, 'white'),
|
||||
(1.5, 5.9, "Co gdy serwis\nnie odpowiada?", False, 'white'),
|
||||
(1.5, 3.6, "Jak zachować\nspójność\ntransakcji?", False, 'white'),
|
||||
(1.5, 1.3, "Jak odnaleźć\nadres serwisu?", False, 'white'),
|
||||
|
||||
# Patterns (right, filled rect, gray)
|
||||
(7.0, 9.3, "Microservices", True, GRAY2),
|
||||
(7.0, 7.0, "API Gateway", True, GRAY2),
|
||||
(7.0, 4.7, "Circuit Breaker", True, GRAY2),
|
||||
(7.0, 2.4, "Saga", True, GRAY2),
|
||||
(10.0, 5.9, "Service\nDiscovery", True, GRAY1),
|
||||
]
|
||||
|
||||
# Draw nodes
|
||||
node_w_prob = 2.8
|
||||
node_h_prob = 1.3
|
||||
node_w_pat = 2.5
|
||||
node_h_pat = 1.0
|
||||
|
||||
for nx, ny, label, is_pattern, fill in nodes:
|
||||
if is_pattern:
|
||||
w, h = node_w_pat, node_h_pat
|
||||
rect = FancyBboxPatch((nx - w/2, ny - h/2), w, h,
|
||||
boxstyle="round,pad=0.1", lw=2,
|
||||
edgecolor=LN, facecolor=fill)
|
||||
ax.add_patch(rect)
|
||||
ax.text(nx, ny, label, ha='center', va='center',
|
||||
fontsize=10, fontweight='bold')
|
||||
else:
|
||||
w, h = node_w_prob, node_h_prob
|
||||
rect = FancyBboxPatch((nx - w/2, ny - h/2), w, h,
|
||||
boxstyle="round,pad=0.1", lw=1.2,
|
||||
edgecolor=LN, facecolor=fill, linestyle='--')
|
||||
ax.add_patch(rect)
|
||||
ax.text(nx, ny, label, ha='center', va='center',
|
||||
fontsize=FS_SMALL, fontstyle='italic')
|
||||
|
||||
# Arrows: problem → pattern (solid), pattern → problem (dashed label)
|
||||
arrows = [
|
||||
# (x1, y1, x2, y2, label, style)
|
||||
(2.9, 10.5, 5.75, 9.5, "rozwiązuje →", '->', 1.5),
|
||||
(7.0, 8.8, 2.9, 8.5, "← rodzi problem", '->', 1.0),
|
||||
(2.9, 8.0, 5.75, 7.2, "rozwiązuje →", '->', 1.5),
|
||||
(7.0, 6.5, 2.9, 6.2, "← rodzi problem", '->', 1.0),
|
||||
(2.9, 5.7, 5.75, 5.0, "rozwiązuje →", '->', 1.5),
|
||||
(7.0, 4.2, 2.9, 3.9, "← rodzi problem", '->', 1.0),
|
||||
(2.9, 3.3, 5.75, 2.6, "rozwiązuje →", '->', 1.5),
|
||||
# Microservices → Service Discovery
|
||||
(8.25, 9.0, 9.5, 6.5, "wymaga →", '->', 1.0),
|
||||
# Problem → Service Discovery
|
||||
(2.9, 1.3, 8.75, 5.6, "rozwiązuje →", '->', 1.2),
|
||||
]
|
||||
|
||||
for x1, y1, x2, y2, label, style, lw in arrows:
|
||||
ax.annotate("", xy=(x2, y2), xytext=(x1, y1),
|
||||
arrowprops=dict(arrowstyle=style, color=LN, lw=lw,
|
||||
connectionstyle="arc3,rad=0.05"))
|
||||
mx, my = (x1 + x2) / 2, (y1 + y2) / 2
|
||||
ax.text(mx, my + 0.2, label, ha='center', va='center',
|
||||
fontsize=6.5, fontstyle='italic', color='#555555',
|
||||
bbox=dict(boxstyle='round,pad=0.1', facecolor='white',
|
||||
edgecolor='none', alpha=0.8))
|
||||
|
||||
# Legend
|
||||
legend_y = 0.3
|
||||
# Problem node
|
||||
r1 = FancyBboxPatch((1.0, legend_y - 0.2), 1.5, 0.4,
|
||||
boxstyle="round,pad=0.05", lw=1, edgecolor=LN,
|
||||
facecolor='white', linestyle='--')
|
||||
ax.add_patch(r1)
|
||||
ax.text(1.75, legend_y, "Problem", ha='center', va='center', fontsize=7)
|
||||
# Pattern node
|
||||
r2 = FancyBboxPatch((3.5, legend_y - 0.2), 1.5, 0.4,
|
||||
boxstyle="round,pad=0.05", lw=1.5, edgecolor=LN,
|
||||
facecolor=GRAY2)
|
||||
ax.add_patch(r2)
|
||||
ax.text(4.25, legend_y, "Wzorzec", ha='center', va='center',
|
||||
fontsize=7, fontweight='bold')
|
||||
ax.text(6.5, legend_y,
|
||||
"Nawigacja: Problem → Wzorzec → Nowy Problem → Wzorzec → ...",
|
||||
ha='left', va='center', fontsize=7, fontstyle='italic')
|
||||
|
||||
fig.tight_layout()
|
||||
out = os.path.join(OUTPUT_DIR, 'q14_pattern_language_navigation.png')
|
||||
fig.savefig(out, dpi=DPI, bbox_inches='tight', facecolor=BG)
|
||||
plt.close(fig)
|
||||
print(f" Saved: {out}")
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Main
|
||||
# ============================================================
|
||||
@ -294,4 +508,6 @@ if __name__ == '__main__':
|
||||
generate_pattern_template()
|
||||
generate_catalog_map()
|
||||
generate_three_pillars()
|
||||
generate_observer_card_filled()
|
||||
generate_pattern_language_navigation()
|
||||
print("Done!")
|
||||
|
||||
1637
pytania/generate_q23_diagrams.py
Normal file
1401
pytania/generate_q24_diagrams.py
Normal file
620
pytania/generate_q31_diagrams.py
Normal file
@ -0,0 +1,620 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Generate diagrams for PYTANIE 31: Interaktywne wspomaganie decyzji w warunkach ryzyka.
|
||||
|
||||
Diagrams:
|
||||
1. Payoff matrix + all criteria results comparison (bar chart)
|
||||
2. Regret matrix construction step-by-step
|
||||
3. Hurwicz α interpolation between maximax and maximin
|
||||
4. Decision criteria mnemonic map
|
||||
5. Expected value criterion with probability-weighted bars
|
||||
6. Decision conditions spectrum (pewność → ryzyko → niepewność)
|
||||
|
||||
All: A4-compatible, B&W, 300 DPI, laser-printer-friendly.
|
||||
"""
|
||||
|
||||
import matplotlib
|
||||
matplotlib.use('Agg')
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.patches as mpatches
|
||||
from matplotlib.patches import FancyBboxPatch
|
||||
import numpy as np
|
||||
import os
|
||||
|
||||
DPI = 300
|
||||
BG = 'white'
|
||||
LN = 'black'
|
||||
FS = 8
|
||||
FS_TITLE = 11
|
||||
OUTPUT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'img')
|
||||
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
||||
|
||||
GRAY1 = '#E8E8E8'
|
||||
GRAY2 = '#D0D0D0'
|
||||
GRAY3 = '#B8B8B8'
|
||||
GRAY4 = '#F5F5F5'
|
||||
GRAY5 = '#C0C0C0'
|
||||
|
||||
|
||||
def draw_box(ax, x, y, w, h, text, fill='white', lw=1.2, fontsize=FS,
|
||||
fontweight='normal', ha='center', va='center', rounded=True):
|
||||
if rounded:
|
||||
rect = FancyBboxPatch((x, y), w, h, boxstyle="round,pad=0.05",
|
||||
lw=lw, edgecolor=LN, facecolor=fill)
|
||||
else:
|
||||
rect = mpatches.Rectangle((x, y), w, h, lw=lw, edgecolor=LN, facecolor=fill)
|
||||
ax.add_patch(rect)
|
||||
ax.text(x + w/2, y + h/2, text, ha=ha, va=va, fontsize=fontsize,
|
||||
fontweight=fontweight, wrap=True)
|
||||
|
||||
|
||||
def draw_arrow(ax, x1, y1, x2, y2, lw=1.2, style='->', color=LN):
|
||||
ax.annotate("", xy=(x2, y2), xytext=(x1, y1),
|
||||
arrowprops=dict(arrowstyle=style, color=color, lw=lw))
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 1. PAYOFF MATRIX + ALL CRITERIA BAR CHART
|
||||
# ============================================================
|
||||
def draw_criteria_comparison():
|
||||
fig, axes = plt.subplots(1, 2, figsize=(8.27, 4.5), gridspec_kw={'width_ratios': [1.2, 1]})
|
||||
|
||||
# -- Left: Payoff matrix as styled table --
|
||||
ax = axes[0]
|
||||
ax.axis('off')
|
||||
ax.set_xlim(0, 6)
|
||||
ax.set_ylim(0, 6)
|
||||
ax.set_title('Macierz wypłat (tys. zł)', fontsize=FS_TITLE, fontweight='bold', pad=8)
|
||||
|
||||
# Headers
|
||||
headers_col = ['', 'S₁\n(dobra)', 'S₂\n(średnia)', 'S₃\n(zła)']
|
||||
rows = [
|
||||
['A₁ (fabryka)', '200', '50', '−100'],
|
||||
['A₂ (sklep)', '80', '70', '40'],
|
||||
['A₃ (obligacje)', '30', '30', '30'],
|
||||
]
|
||||
|
||||
col_w = [1.8, 1.2, 1.2, 1.2]
|
||||
row_h = 0.7
|
||||
start_y = 4.5
|
||||
start_x = 0.2
|
||||
|
||||
# Draw header row
|
||||
x = start_x
|
||||
for j, h in enumerate(headers_col):
|
||||
fill = GRAY2 if j > 0 else GRAY3
|
||||
rect = mpatches.Rectangle((x, start_y), col_w[j], row_h, lw=1, edgecolor=LN, facecolor=fill)
|
||||
ax.add_patch(rect)
|
||||
ax.text(x + col_w[j]/2, start_y + row_h/2, h, ha='center', va='center',
|
||||
fontsize=FS, fontweight='bold')
|
||||
x += col_w[j]
|
||||
|
||||
# Draw data rows
|
||||
for i, row in enumerate(rows):
|
||||
x = start_x
|
||||
y = start_y - (i+1) * row_h
|
||||
for j, val in enumerate(row):
|
||||
fill = GRAY4 if j == 0 else ('white' if i % 2 == 0 else GRAY1)
|
||||
# Highlight negative
|
||||
if val.startswith('−'):
|
||||
fill = '#D8D8D8'
|
||||
rect = mpatches.Rectangle((x, y), col_w[j], row_h, lw=1, edgecolor=LN, facecolor=fill)
|
||||
ax.add_patch(rect)
|
||||
fw = 'bold' if j == 0 else 'normal'
|
||||
ax.text(x + col_w[j]/2, y + row_h/2, val, ha='center', va='center',
|
||||
fontsize=FS, fontweight=fw)
|
||||
x += col_w[j]
|
||||
|
||||
# Probability row for EV
|
||||
x = start_x
|
||||
y = start_y - 4 * row_h
|
||||
probs = ['p (dla E[X]):', '0.5', '0.3', '0.2']
|
||||
for j, val in enumerate(probs):
|
||||
fill = GRAY5 if j > 0 else GRAY3
|
||||
rect = mpatches.Rectangle((x, y), col_w[j], row_h * 0.7, lw=1, edgecolor=LN, facecolor=fill)
|
||||
ax.add_patch(rect)
|
||||
ax.text(x + col_w[j]/2, y + row_h*0.35, val, ha='center', va='center',
|
||||
fontsize=7, fontweight='bold', style='italic')
|
||||
x += col_w[j]
|
||||
|
||||
# -- Right: Bar chart comparing criteria results --
|
||||
ax2 = axes[1]
|
||||
criteria = ['E[X]', 'Laplace', 'Maximax', 'Maximin', 'Hurwicz\nα=0.6', 'Savage']
|
||||
|
||||
# Recalculate with probabilities 0.5, 0.3, 0.2
|
||||
# E[X]: A1=200*0.5+50*0.3+(-100)*0.2=100+15-20=95
|
||||
# A2=80*0.5+70*0.3+40*0.2=40+21+8=69
|
||||
# A3=30*0.5+30*0.3+30*0.2=15+9+6=30
|
||||
ev = [95, 69, 30]
|
||||
laplace = [50, 63.3, 30]
|
||||
maximax = [200, 80, 30]
|
||||
maximin = [-100, 40, 30]
|
||||
hurwicz = [80, 64, 30] # α=0.6
|
||||
savage_maxregret = [140, 120, 170] # lower = better
|
||||
|
||||
# Which alternative wins for each criterion?
|
||||
winners = [0, 1, 0, 1, 0, 1] # index of winning alternative
|
||||
winner_vals = [95, 63.3, 200, 40, 80, 120]
|
||||
|
||||
# Display as grouped bar chart - each criterion shows the 3 alternatives
|
||||
x_pos = np.arange(len(criteria))
|
||||
width = 0.22
|
||||
hatches = ['///', '...', 'xxx']
|
||||
labels = ['A₁ (fabryka)', 'A₂ (sklep)', 'A₃ (obligacje)']
|
||||
|
||||
all_vals = [
|
||||
[ev[0], laplace[0], maximax[0], maximin[0], hurwicz[0], savage_maxregret[0]],
|
||||
[ev[1], laplace[1], maximax[1], maximin[1], hurwicz[1], savage_maxregret[1]],
|
||||
[ev[2], laplace[2], maximax[2], maximin[2], hurwicz[2], savage_maxregret[2]],
|
||||
]
|
||||
|
||||
for i in range(3):
|
||||
bars = ax2.bar(x_pos + (i - 1) * width, all_vals[i], width,
|
||||
label=labels[i], color='white', edgecolor=LN, hatch=hatches[i], lw=0.8)
|
||||
|
||||
# Mark winners with star
|
||||
for c_idx in range(len(criteria)):
|
||||
w = winners[c_idx]
|
||||
val = all_vals[w][c_idx]
|
||||
ax2.text(x_pos[c_idx] + (w - 1) * width, val + 5, '★',
|
||||
ha='center', va='bottom', fontsize=10, fontweight='bold')
|
||||
|
||||
ax2.set_xticks(x_pos)
|
||||
ax2.set_xticklabels(criteria, fontsize=7)
|
||||
ax2.set_ylabel('Wartość kryterium', fontsize=8)
|
||||
ax2.set_title('Porównanie kryteriów', fontsize=FS_TITLE, fontweight='bold', pad=8)
|
||||
ax2.legend(fontsize=7, loc='upper right')
|
||||
ax2.axhline(y=0, color=LN, lw=0.5, ls='-')
|
||||
ax2.spines['top'].set_visible(False)
|
||||
ax2.spines['right'].set_visible(False)
|
||||
ax2.tick_params(labelsize=7)
|
||||
|
||||
# Note about Savage
|
||||
ax2.text(5, -30, '(Savage: niżej\n= lepiej)', fontsize=6, ha='center',
|
||||
va='top', style='italic')
|
||||
|
||||
plt.tight_layout()
|
||||
outpath = os.path.join(OUTPUT_DIR, 'q31_criteria_comparison.png')
|
||||
fig.savefig(outpath, dpi=DPI, bbox_inches='tight', facecolor=BG)
|
||||
plt.close(fig)
|
||||
print(f" Saved: {outpath}")
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 2. REGRET MATRIX CONSTRUCTION
|
||||
# ============================================================
|
||||
def draw_regret_matrix():
|
||||
fig, ax = plt.subplots(1, 1, figsize=(8.27, 5))
|
||||
ax.axis('off')
|
||||
ax.set_xlim(0, 10)
|
||||
ax.set_ylim(0, 7)
|
||||
ax.set_title('Kryterium Savage\'a — budowa macierzy żalu',
|
||||
fontsize=FS_TITLE + 1, fontweight='bold', pad=10)
|
||||
|
||||
# --- Step 1: Original payoff matrix (left) ---
|
||||
ax.text(2.2, 6.3, 'Krok 1: Macierz wypłat', fontsize=9, fontweight='bold',
|
||||
ha='center', va='center')
|
||||
|
||||
col_w = 1.0
|
||||
row_h = 0.55
|
||||
headers = ['', 'S₁', 'S₂', 'S₃']
|
||||
data = [
|
||||
['A₁', '200', '50', '−100'],
|
||||
['A₂', '80', '70', '40'],
|
||||
['A₃', '30', '30', '30'],
|
||||
]
|
||||
start_x = 0.3
|
||||
start_y = 5.5
|
||||
|
||||
for j, h in enumerate(headers):
|
||||
w = 0.7 if j == 0 else col_w
|
||||
x = start_x + (0 if j == 0 else 0.7 + (j-1)*col_w)
|
||||
rect = mpatches.Rectangle((x, start_y), w, row_h, lw=1, edgecolor=LN, facecolor=GRAY2)
|
||||
ax.add_patch(rect)
|
||||
ax.text(x + w/2, start_y + row_h/2, h, ha='center', va='center',
|
||||
fontsize=FS, fontweight='bold')
|
||||
|
||||
for i, row in enumerate(data):
|
||||
y = start_y - (i+1) * row_h
|
||||
for j, val in enumerate(row):
|
||||
w = 0.7 if j == 0 else col_w
|
||||
x = start_x + (0 if j == 0 else 0.7 + (j-1)*col_w)
|
||||
fill = GRAY4 if j == 0 else 'white'
|
||||
rect = mpatches.Rectangle((x, y), w, row_h, lw=1, edgecolor=LN, facecolor=fill)
|
||||
ax.add_patch(rect)
|
||||
ax.text(x + w/2, y + row_h/2, val, ha='center', va='center', fontsize=FS)
|
||||
|
||||
# Max per column annotation
|
||||
max_y = start_y - 3 * row_h - 0.1
|
||||
ax.text(start_x + 0.7 + 0.5*col_w, max_y, 'max=200', fontsize=7,
|
||||
ha='center', va='top', fontweight='bold', color='#333')
|
||||
ax.text(start_x + 0.7 + 1.5*col_w, max_y, 'max=70', fontsize=7,
|
||||
ha='center', va='top', fontweight='bold', color='#333')
|
||||
ax.text(start_x + 0.7 + 2.5*col_w, max_y, 'max=40', fontsize=7,
|
||||
ha='center', va='top', fontweight='bold', color='#333')
|
||||
|
||||
# Arrow
|
||||
ax.annotate("", xy=(5.0, 4.8), xytext=(4.2, 4.8),
|
||||
arrowprops=dict(arrowstyle='->', color=LN, lw=2))
|
||||
ax.text(4.6, 5.0, 'rᵢⱼ = max − aᵢⱼ', fontsize=8, ha='center', va='bottom',
|
||||
fontweight='bold')
|
||||
|
||||
# --- Step 2: Regret matrix (right) ---
|
||||
ax.text(7.5, 6.3, 'Krok 2: Macierz żalu', fontsize=9, fontweight='bold',
|
||||
ha='center', va='center')
|
||||
|
||||
regret_data = [
|
||||
['A₁', '0', '20', '140'],
|
||||
['A₂', '120', '0', '0'],
|
||||
['A₃', '170', '40', '10'],
|
||||
]
|
||||
headers2 = ['', 'S₁', 'S₂', 'S₃', 'max rᵢ']
|
||||
start_x2 = 5.3
|
||||
|
||||
for j, h in enumerate(headers2):
|
||||
w = 0.7 if j == 0 else (0.9 if j < 4 else 1.0)
|
||||
x = start_x2
|
||||
if j == 0:
|
||||
x = start_x2
|
||||
elif j <= 3:
|
||||
x = start_x2 + 0.7 + (j-1)*0.9
|
||||
else:
|
||||
x = start_x2 + 0.7 + 3*0.9
|
||||
rect = mpatches.Rectangle((x, start_y), w, row_h, lw=1, edgecolor=LN,
|
||||
facecolor=GRAY2 if j < 4 else GRAY3)
|
||||
ax.add_patch(rect)
|
||||
ax.text(x + w/2, start_y + row_h/2, h, ha='center', va='center',
|
||||
fontsize=FS, fontweight='bold')
|
||||
|
||||
max_regrets = [140, 120, 170]
|
||||
for i, row in enumerate(regret_data):
|
||||
y = start_y - (i+1) * row_h
|
||||
for j, val in enumerate(row):
|
||||
w = 0.7 if j == 0 else 0.9
|
||||
x = start_x2 + (0 if j == 0 else 0.7 + (j-1)*0.9)
|
||||
fill = GRAY4 if j == 0 else 'white'
|
||||
# Highlight the max regret cell
|
||||
if j > 0 and int(val) == max_regrets[i]:
|
||||
fill = GRAY2
|
||||
rect = mpatches.Rectangle((x, y), w, row_h, lw=1, edgecolor=LN, facecolor=fill)
|
||||
ax.add_patch(rect)
|
||||
fw = 'bold' if (j > 0 and int(val) == max_regrets[i]) else 'normal'
|
||||
ax.text(x + w/2, y + row_h/2, val, ha='center', va='center',
|
||||
fontsize=FS, fontweight=fw)
|
||||
|
||||
# Max regret column
|
||||
x = start_x2 + 0.7 + 3*0.9
|
||||
w = 1.0
|
||||
fill = '#C8C8C8' if max_regrets[i] == min(max_regrets) else GRAY1
|
||||
rect = mpatches.Rectangle((x, y), w, row_h, lw=1.5 if max_regrets[i]==min(max_regrets) else 1,
|
||||
edgecolor=LN, facecolor=fill)
|
||||
ax.add_patch(rect)
|
||||
marker = ' ★' if max_regrets[i] == min(max_regrets) else ''
|
||||
ax.text(x + w/2, y + row_h/2, f'{max_regrets[i]}{marker}', ha='center', va='center',
|
||||
fontsize=FS, fontweight='bold')
|
||||
|
||||
# Bottom conclusion
|
||||
ax.text(5.0, 2.8, 'Krok 3: Wybierz min z max żalu → A₂ (max żal = 120)',
|
||||
fontsize=10, ha='center', va='center', fontweight='bold',
|
||||
bbox=dict(boxstyle='round,pad=0.3', facecolor=GRAY1, edgecolor=LN, lw=1.5))
|
||||
|
||||
# Interpretatation examples
|
||||
ax.text(5.0, 2.0, 'Interpretacja żalu: r₁₃ = 140 oznacza:\n'
|
||||
'„Gdyby nastąpił S₃ (zła koniunktura), a wybrałbym A₁,\n'
|
||||
'żałowałbym, bo najlepszą opcją byłoby A₂ z wynikiem 40 — traciłbym 140"',
|
||||
fontsize=7.5, ha='center', va='center', style='italic',
|
||||
bbox=dict(boxstyle='round,pad=0.3', facecolor=GRAY4, edgecolor=GRAY3, lw=0.8))
|
||||
|
||||
# Mnemonic
|
||||
ax.text(5.0, 0.8, 'Mnemonik: Savage = „Żal jak nóż"\nMaksymalny żal to nóż '
|
||||
'— wybierz opcję z NAJMNIEJSZYM nożem',
|
||||
fontsize=8, ha='center', va='center', fontweight='bold',
|
||||
bbox=dict(boxstyle='round,pad=0.3', facecolor='white', edgecolor=LN, lw=1))
|
||||
|
||||
plt.tight_layout()
|
||||
outpath = os.path.join(OUTPUT_DIR, 'q31_regret_matrix.png')
|
||||
fig.savefig(outpath, dpi=DPI, bbox_inches='tight', facecolor=BG)
|
||||
plt.close(fig)
|
||||
print(f" Saved: {outpath}")
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 3. HURWICZ α INTERPOLATION
|
||||
# ============================================================
|
||||
def draw_hurwicz_interpolation():
|
||||
fig, ax = plt.subplots(1, 1, figsize=(8.27, 4))
|
||||
ax.set_title('Kryterium Hurwicza — wpływ α na wybór alternatywy',
|
||||
fontsize=FS_TITLE + 1, fontweight='bold', pad=10)
|
||||
|
||||
alphas = np.linspace(0, 1, 200)
|
||||
|
||||
# V(Ai) = α * max_i + (1-α) * min_i
|
||||
# A1: max=200, min=-100
|
||||
# A2: max=80, min=40
|
||||
# A3: max=30, min=30
|
||||
v1 = alphas * 200 + (1 - alphas) * (-100)
|
||||
v2 = alphas * 80 + (1 - alphas) * 40
|
||||
v3 = alphas * 30 + (1 - alphas) * 30
|
||||
|
||||
ax.plot(alphas, v1, 'k-', lw=2, label='A₁ (fabryka): V = 300α − 100')
|
||||
ax.plot(alphas, v2, 'k--', lw=2, label='A₂ (sklep): V = 40α + 40')
|
||||
ax.plot(alphas, v3, 'k:', lw=2, label='A₃ (obligacje): V = 30')
|
||||
|
||||
# Find crossover points
|
||||
# A2 = A1: 40α + 40 = 300α - 100 → 140 = 260α → α = 140/260 ≈ 0.538
|
||||
alpha_cross_12 = 140 / 260
|
||||
v_cross_12 = 40 * alpha_cross_12 + 40
|
||||
|
||||
# A2 = A3: 40α + 40 = 30 → 40α = -10 → α = -0.25 (never — A2 always > A3)
|
||||
# A1 = A3: 300α - 100 = 30 → 300α = 130 → α = 130/300 ≈ 0.433
|
||||
alpha_cross_13 = 130 / 300
|
||||
v_cross_13 = 30
|
||||
|
||||
ax.plot(alpha_cross_12, v_cross_12, 'ko', markersize=8, zorder=5)
|
||||
ax.annotate(f'α ≈ {alpha_cross_12:.2f}\nA₁ = A₂', xy=(alpha_cross_12, v_cross_12),
|
||||
xytext=(alpha_cross_12 + 0.12, v_cross_12 - 30),
|
||||
fontsize=8, fontweight='bold',
|
||||
arrowprops=dict(arrowstyle='->', color=LN, lw=1))
|
||||
|
||||
# Shade winning regions
|
||||
ax.axvspan(0, alpha_cross_12, alpha=0.08, color='black', label='_')
|
||||
ax.axvspan(alpha_cross_12, 1, alpha=0.15, color='black', label='_')
|
||||
|
||||
ax.text(alpha_cross_12 / 2, -60, 'A₂ wygrywa\n(pesymistycznie)',
|
||||
fontsize=8, ha='center', va='center',
|
||||
bbox=dict(boxstyle='round', facecolor='white', edgecolor=LN))
|
||||
ax.text((alpha_cross_12 + 1) / 2, 160, 'A₁ wygrywa\n(optymistycznie)',
|
||||
fontsize=8, ha='center', va='center',
|
||||
bbox=dict(boxstyle='round', facecolor='white', edgecolor=LN))
|
||||
|
||||
# Special α values
|
||||
ax.axvline(x=0, color=LN, lw=0.5, ls=':')
|
||||
ax.axvline(x=1, color=LN, lw=0.5, ls=':')
|
||||
ax.text(0, -115, 'α=0\nmaximin', fontsize=7, ha='center', va='top', fontweight='bold')
|
||||
ax.text(1, -115, 'α=1\nmaximax', fontsize=7, ha='center', va='top', fontweight='bold')
|
||||
|
||||
ax.set_xlabel('Współczynnik optymizmu α', fontsize=9)
|
||||
ax.set_ylabel('V(Aᵢ) = α·max + (1−α)·min', fontsize=9)
|
||||
ax.legend(fontsize=8, loc='upper left')
|
||||
ax.spines['top'].set_visible(False)
|
||||
ax.spines['right'].set_visible(False)
|
||||
ax.set_xlim(-0.05, 1.05)
|
||||
ax.axhline(y=0, color=LN, lw=0.3, ls='-')
|
||||
ax.tick_params(labelsize=8)
|
||||
|
||||
plt.tight_layout()
|
||||
outpath = os.path.join(OUTPUT_DIR, 'q31_hurwicz_alpha.png')
|
||||
fig.savefig(outpath, dpi=DPI, bbox_inches='tight', facecolor=BG)
|
||||
plt.close(fig)
|
||||
print(f" Saved: {outpath}")
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 4. DECISION CRITERIA MNEMONIC MAP
|
||||
# ============================================================
|
||||
def draw_criteria_mnemonic():
|
||||
fig, ax = plt.subplots(1, 1, figsize=(8.27, 6))
|
||||
ax.set_xlim(0, 10)
|
||||
ax.set_ylim(0, 8)
|
||||
ax.set_aspect('equal')
|
||||
ax.axis('off')
|
||||
ax.set_title('Mapa mnemoniczna — 6 kryteriów decyzyjnych',
|
||||
fontsize=FS_TITLE + 2, fontweight='bold', pad=10)
|
||||
|
||||
# Central node
|
||||
draw_box(ax, 3.5, 3.5, 3, 1, 'MACIERZ\nWYPŁAT', fill=GRAY2, lw=2,
|
||||
fontsize=11, fontweight='bold')
|
||||
|
||||
# Criteria boxes around the center
|
||||
criteria = [
|
||||
# (x, y, w, h, title, mnemonic, formula)
|
||||
(0, 6.5, 3, 1.2, 'WARTOŚĆ OCZEKIWANA', '„Mam prawdopodobieństwa"', 'E[Aᵢ] = Σ pⱼ·aᵢⱼ'),
|
||||
(3.5, 6.5, 3, 1.2, 'LAPLACE', '„Wszystko po równo"', 'V = Σaᵢⱼ / n'),
|
||||
(7, 6.5, 3, 1.2, 'MAXIMAX', '„Optymista: max z max"', 'max maxⱼ aᵢⱼ'),
|
||||
(0, 0.5, 3, 1.2, 'MAXIMIN (Wald)', '„Pesymista: max z min"', 'max minⱼ aᵢⱼ'),
|
||||
(3.5, 0.5, 3, 1.2, 'HURWICZ', '„α pomiędzy"', 'α·max + (1−α)·min'),
|
||||
(7, 0.5, 3, 1.2, 'SAVAGE', '„Min max żalu"', 'min maxⱼ rᵢⱼ'),
|
||||
]
|
||||
|
||||
fills = [GRAY3, GRAY1, 'white', 'white', GRAY1, GRAY3]
|
||||
|
||||
for i, (x, y, w, h, title, mnem, formula) in enumerate(criteria):
|
||||
rect = FancyBboxPatch((x, y), w, h, boxstyle="round,pad=0.08",
|
||||
lw=1.5, edgecolor=LN, facecolor=fills[i])
|
||||
ax.add_patch(rect)
|
||||
ax.text(x + w/2, y + h*0.78, title, ha='center', va='center',
|
||||
fontsize=8, fontweight='bold')
|
||||
ax.text(x + w/2, y + h*0.45, mnem, ha='center', va='center',
|
||||
fontsize=7, style='italic')
|
||||
ax.text(x + w/2, y + h*0.15, formula, ha='center', va='center',
|
||||
fontsize=7, fontweight='bold', family='monospace')
|
||||
|
||||
# Arrows from center to each box
|
||||
cx, cy = 5, 4 # center of macierz
|
||||
bx, by = x + w/2, y + h/2
|
||||
if by > cy:
|
||||
ax.annotate("", xy=(bx, y), xytext=(cx, 4.5),
|
||||
arrowprops=dict(arrowstyle='->', color=LN, lw=1,
|
||||
connectionstyle='arc3,rad=0'))
|
||||
else:
|
||||
ax.annotate("", xy=(bx, y + h), xytext=(cx, 3.5),
|
||||
arrowprops=dict(arrowstyle='->', color=LN, lw=1,
|
||||
connectionstyle='arc3,rad=0'))
|
||||
|
||||
# Labels on arrows
|
||||
ax.text(1.2, 5.6, 'znane p', fontsize=7, ha='center', va='center',
|
||||
bbox=dict(boxstyle='round,pad=0.15', facecolor='white', edgecolor=GRAY3, lw=0.5))
|
||||
ax.text(5, 5.6, 'p = 1/n', fontsize=7, ha='center', va='center',
|
||||
bbox=dict(boxstyle='round,pad=0.15', facecolor='white', edgecolor=GRAY3, lw=0.5))
|
||||
ax.text(8.7, 5.6, 'max ↑', fontsize=7, ha='center', va='center',
|
||||
bbox=dict(boxstyle='round,pad=0.15', facecolor='white', edgecolor=GRAY3, lw=0.5))
|
||||
ax.text(1.2, 2.5, 'min ↑', fontsize=7, ha='center', va='center',
|
||||
bbox=dict(boxstyle='round,pad=0.15', facecolor='white', edgecolor=GRAY3, lw=0.5))
|
||||
ax.text(5, 2.5, 'podaj α', fontsize=7, ha='center', va='center',
|
||||
bbox=dict(boxstyle='round,pad=0.15', facecolor='white', edgecolor=GRAY3, lw=0.5))
|
||||
ax.text(8.7, 2.5, 'macierz\nżalu', fontsize=7, ha='center', va='center',
|
||||
bbox=dict(boxstyle='round,pad=0.15', facecolor='white', edgecolor=GRAY3, lw=0.5))
|
||||
|
||||
plt.tight_layout()
|
||||
outpath = os.path.join(OUTPUT_DIR, 'q31_criteria_mnemonic.png')
|
||||
fig.savefig(outpath, dpi=DPI, bbox_inches='tight', facecolor=BG)
|
||||
plt.close(fig)
|
||||
print(f" Saved: {outpath}")
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 5. EXPECTED VALUE CRITERION WITH PROBABILITY BARS
|
||||
# ============================================================
|
||||
def draw_expected_value():
|
||||
fig, axes = plt.subplots(1, 3, figsize=(8.27, 3.5), sharey=True)
|
||||
fig.suptitle('Kryterium wartości oczekiwanej E[X] — rozkład wyników per alternatywa',
|
||||
fontsize=FS_TITLE, fontweight='bold', y=1.02)
|
||||
|
||||
# Probabilities: p1=0.5, p2=0.3, p3=0.2
|
||||
probs = [0.5, 0.3, 0.2]
|
||||
states = ['S₁\n(dobra)\np=0.5', 'S₂\n(średnia)\np=0.3', 'S₃\n(zła)\np=0.2']
|
||||
alts = [
|
||||
('A₁ (fabryka)', [200, 50, -100], 95),
|
||||
('A₂ (sklep)', [80, 70, 40], 69),
|
||||
('A₃ (obligacje)', [30, 30, 30], 30),
|
||||
]
|
||||
|
||||
hatches = ['///', '...', 'xxx']
|
||||
|
||||
for idx, (ax, (name, vals, ev)) in enumerate(zip(axes, alts)):
|
||||
# Bar: height = payoff, width proportional to probability
|
||||
x_positions = [0, 0.6, 1.0]
|
||||
widths = [p * 0.9 for p in probs]
|
||||
|
||||
for i, (v, p, h) in enumerate(zip(vals, probs, hatches)):
|
||||
color = 'white' if v >= 0 else GRAY2
|
||||
bar = ax.bar(x_positions[i], v, width=widths[i], color=color,
|
||||
edgecolor=LN, hatch=h, lw=0.8, align='edge')
|
||||
# Value label
|
||||
offset = 8 if v >= 0 else -12
|
||||
ax.text(x_positions[i] + widths[i]/2, v + offset, f'{v}',
|
||||
ha='center', va='center', fontsize=8, fontweight='bold')
|
||||
# Probability contribution
|
||||
contrib = v * p
|
||||
ax.text(x_positions[i] + widths[i]/2, v/2,
|
||||
f'{v}×{p}\n={contrib:.0f}', ha='center', va='center',
|
||||
fontsize=6, style='italic')
|
||||
|
||||
# Expected value line
|
||||
ax.axhline(y=ev, color=LN, lw=2, ls='--')
|
||||
ax.text(1.35, ev, f'E[X]={ev}', fontsize=8, fontweight='bold',
|
||||
va='center', ha='left',
|
||||
bbox=dict(boxstyle='round,pad=0.15', facecolor=GRAY1, edgecolor=LN))
|
||||
|
||||
ax.set_title(name, fontsize=9, fontweight='bold')
|
||||
ax.set_xticks([0.225, 0.735, 1.09])
|
||||
ax.set_xticklabels(['S₁', 'S₂', 'S₃'], fontsize=7)
|
||||
ax.axhline(y=0, color=LN, lw=0.5)
|
||||
ax.spines['top'].set_visible(False)
|
||||
ax.spines['right'].set_visible(False)
|
||||
ax.tick_params(labelsize=7)
|
||||
|
||||
# Star on winner
|
||||
if ev == 95:
|
||||
ax.text(0.7, ev + 20, '★ MAX', fontsize=9, fontweight='bold',
|
||||
ha='center', va='bottom')
|
||||
|
||||
axes[0].set_ylabel('Wypłata (tys. zł)', fontsize=8)
|
||||
|
||||
plt.tight_layout()
|
||||
outpath = os.path.join(OUTPUT_DIR, 'q31_expected_value.png')
|
||||
fig.savefig(outpath, dpi=DPI, bbox_inches='tight', facecolor=BG)
|
||||
plt.close(fig)
|
||||
print(f" Saved: {outpath}")
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 6. DECISION CONDITIONS SPECTRUM
|
||||
# ============================================================
|
||||
def draw_conditions_spectrum():
|
||||
fig, ax = plt.subplots(1, 1, figsize=(8.27, 3.5))
|
||||
ax.set_xlim(0, 10)
|
||||
ax.set_ylim(0, 5)
|
||||
ax.set_aspect('equal')
|
||||
ax.axis('off')
|
||||
ax.set_title('Warunki decyzyjne — spektrum wiedzy decydenta',
|
||||
fontsize=FS_TITLE + 1, fontweight='bold', pad=10)
|
||||
|
||||
# Three zones
|
||||
zones = [
|
||||
(0.3, 1.5, 2.8, 2.5, 'PEWNOŚĆ', 'white', [
|
||||
'Znamy dokładny wynik',
|
||||
'Przykład: lokata 5%',
|
||||
'Metoda: po prostu wybierz',
|
||||
'najlepszy wynik'
|
||||
]),
|
||||
(3.5, 1.5, 2.8, 2.5, 'RYZYKO', GRAY1, [
|
||||
'Znamy wyniki I prawdop.',
|
||||
'Przykład: gra w kości',
|
||||
'Metoda: wartość',
|
||||
'oczekiwana E[X]'
|
||||
]),
|
||||
(6.7, 1.5, 2.8, 2.5, 'NIEPEWNOŚĆ', GRAY3, [
|
||||
'Znamy wyniki, ale',
|
||||
'NIE znamy prawdop.',
|
||||
'Metody: Laplace, maximax,',
|
||||
'maximin, Hurwicz, Savage'
|
||||
]),
|
||||
]
|
||||
|
||||
for x, y, w, h, title, fill, lines in zones:
|
||||
rect = FancyBboxPatch((x, y), w, h, boxstyle="round,pad=0.1",
|
||||
lw=2, edgecolor=LN, facecolor=fill)
|
||||
ax.add_patch(rect)
|
||||
ax.text(x + w/2, y + h - 0.3, title, ha='center', va='center',
|
||||
fontsize=11, fontweight='bold')
|
||||
for i, line in enumerate(lines):
|
||||
ax.text(x + w/2, y + h - 0.7 - i*0.4, line, ha='center', va='center',
|
||||
fontsize=7)
|
||||
|
||||
# Arrows between zones
|
||||
ax.annotate("", xy=(3.4, 2.75), xytext=(3.15, 2.75),
|
||||
arrowprops=dict(arrowstyle='->', color=LN, lw=2))
|
||||
ax.annotate("", xy=(6.6, 2.75), xytext=(6.35, 2.75),
|
||||
arrowprops=dict(arrowstyle='->', color=LN, lw=2))
|
||||
|
||||
# Bottom: knowledge gradient bar
|
||||
gradient_y = 0.5
|
||||
gradient_h = 0.5
|
||||
n_steps = 50
|
||||
for i in range(n_steps):
|
||||
x = 0.3 + i * (9.2 / n_steps)
|
||||
w = 9.2 / n_steps + 0.01
|
||||
gray_val = 1 - (i / n_steps) * 0.7
|
||||
rect = mpatches.Rectangle((x, gradient_y), w, gradient_h, lw=0,
|
||||
facecolor=str(gray_val))
|
||||
ax.add_patch(rect)
|
||||
|
||||
rect = mpatches.Rectangle((0.3, gradient_y), 9.2, gradient_h, lw=1.5,
|
||||
edgecolor=LN, facecolor='none')
|
||||
ax.add_patch(rect)
|
||||
|
||||
ax.text(0.3, gradient_y - 0.15, 'Dużo wiedzy', fontsize=7, ha='left', va='top')
|
||||
ax.text(9.5, gradient_y - 0.15, 'Mało wiedzy', fontsize=7, ha='right', va='top')
|
||||
ax.text(4.95, gradient_y + gradient_h / 2, 'POZIOM WIEDZY DECYDENTA',
|
||||
fontsize=8, fontweight='bold', ha='center', va='center', color='white')
|
||||
|
||||
plt.tight_layout()
|
||||
outpath = os.path.join(OUTPUT_DIR, 'q31_conditions_spectrum.png')
|
||||
fig.savefig(outpath, dpi=DPI, bbox_inches='tight', facecolor=BG)
|
||||
plt.close(fig)
|
||||
print(f" Saved: {outpath}")
|
||||
|
||||
|
||||
# ============================================================
|
||||
# MAIN
|
||||
# ============================================================
|
||||
if __name__ == '__main__':
|
||||
print("Generating PYTANIE 31 diagrams...")
|
||||
draw_criteria_comparison()
|
||||
draw_regret_matrix()
|
||||
draw_hurwicz_interpolation()
|
||||
draw_criteria_mnemonic()
|
||||
draw_expected_value()
|
||||
draw_conditions_spectrum()
|
||||
print("Done! All Q31 diagrams saved to:", OUTPUT_DIR)
|
||||
BIN
pytania/img/bellman_ford_negative_cycle.png
Normal file
|
After Width: | Height: | Size: 386 KiB |
BIN
pytania/img/bellman_ford_negative_weights.png
Normal file
|
After Width: | Height: | Size: 583 KiB |
BIN
pytania/img/q14_observer_card_filled.png
Normal file
|
After Width: | Height: | Size: 351 KiB |
BIN
pytania/img/q14_pattern_language_navigation.png
Normal file
|
After Width: | Height: | Size: 316 KiB |
BIN
pytania/img/q23_diy_thresholding.png
Normal file
|
After Width: | Height: | Size: 253 KiB |
BIN
pytania/img/q23_diy_unet.png
Normal file
|
After Width: | Height: | Size: 320 KiB |
BIN
pytania/img/q23_dot_product.png
Normal file
|
After Width: | Height: | Size: 214 KiB |
BIN
pytania/img/q23_fc_vs_conv1x1.png
Normal file
|
After Width: | Height: | Size: 319 KiB |
BIN
pytania/img/q23_mean_shift.png
Normal file
|
After Width: | Height: | Size: 422 KiB |
BIN
pytania/img/q23_mnemonics.png
Normal file
|
After Width: | Height: | Size: 490 KiB |
BIN
pytania/img/q23_normalized_cuts.png
Normal file
|
After Width: | Height: | Size: 303 KiB |
BIN
pytania/img/q23_otsu_bimodal.png
Normal file
|
After Width: | Height: | Size: 188 KiB |
BIN
pytania/img/q23_receptive_field.png
Normal file
|
After Width: | Height: | Size: 224 KiB |
BIN
pytania/img/q23_region_growing.png
Normal file
|
After Width: | Height: | Size: 251 KiB |
BIN
pytania/img/q23_relu.png
Normal file
|
After Width: | Height: | Size: 197 KiB |
BIN
pytania/img/q23_transformer_attention.png
Normal file
|
After Width: | Height: | Size: 204 KiB |
BIN
pytania/img/q23_unet_arch.png
Normal file
|
After Width: | Height: | Size: 250 KiB |
BIN
pytania/img/q23_watershed.png
Normal file
|
After Width: | Height: | Size: 294 KiB |
BIN
pytania/img/q24_anchor_boxes.png
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
pytania/img/q24_cnn_architecture.png
Normal file
|
After Width: | Height: | Size: 119 KiB |
BIN
pytania/img/q24_detection_tasks.png
Normal file
|
After Width: | Height: | Size: 163 KiB |
BIN
pytania/img/q24_detector_from_classifier.png
Normal file
|
After Width: | Height: | Size: 229 KiB |
BIN
pytania/img/q24_detr_pipeline.png
Normal file
|
After Width: | Height: | Size: 150 KiB |
BIN
pytania/img/q24_fpn.png
Normal file
|
After Width: | Height: | Size: 157 KiB |
BIN
pytania/img/q24_haar_features.png
Normal file
|
After Width: | Height: | Size: 116 KiB |
BIN
pytania/img/q24_hog_gradient_steps.png
Normal file
|
After Width: | Height: | Size: 131 KiB |
BIN
pytania/img/q24_hog_svm_pipeline.png
Normal file
|
After Width: | Height: | Size: 125 KiB |
BIN
pytania/img/q24_integral_image.png
Normal file
|
After Width: | Height: | Size: 108 KiB |
BIN
pytania/img/q24_iou_diagram.png
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
pytania/img/q24_nms_steps.png
Normal file
|
After Width: | Height: | Size: 175 KiB |
BIN
pytania/img/q24_rcnn_evolution.png
Normal file
|
After Width: | Height: | Size: 201 KiB |
BIN
pytania/img/q24_roi_pooling.png
Normal file
|
After Width: | Height: | Size: 91 KiB |
BIN
pytania/img/q24_sliding_window.png
Normal file
|
After Width: | Height: | Size: 156 KiB |
BIN
pytania/img/q24_svm_hyperplane.png
Normal file
|
After Width: | Height: | Size: 138 KiB |
BIN
pytania/img/q24_two_vs_one_stage.png
Normal file
|
After Width: | Height: | Size: 109 KiB |
BIN
pytania/img/q24_viola_jones_cascade.png
Normal file
|
After Width: | Height: | Size: 131 KiB |
BIN
pytania/img/q24_yolo_grid.png
Normal file
|
After Width: | Height: | Size: 157 KiB |
BIN
pytania/img/q31_conditions_spectrum.png
Normal file
|
After Width: | Height: | Size: 133 KiB |
BIN
pytania/img/q31_criteria_comparison.png
Normal file
|
After Width: | Height: | Size: 176 KiB |
BIN
pytania/img/q31_criteria_mnemonic.png
Normal file
|
After Width: | Height: | Size: 205 KiB |
BIN
pytania/img/q31_expected_value.png
Normal file
|
After Width: | Height: | Size: 149 KiB |
BIN
pytania/img/q31_hurwicz_alpha.png
Normal file
|
After Width: | Height: | Size: 194 KiB |
BIN
pytania/img/q31_regret_matrix.png
Normal file
|
After Width: | Height: | Size: 187 KiB |
@ -193,6 +193,10 @@ Przykład — graf z ujemnymi wagami (Dijkstra daje ZŁY wynik, B-F poprawny):
|
||||
Po V−1 iteracjach dist nadal maleje → V-ta iteracja:
|
||||
dist[src] + weight < dist[dst] → return None
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
**A\*** (graph jak Dijkstra; heuristic = h(v) → oszacowanie odl. do celu):
|
||||
|
||||
@ -85,31 +85,75 @@
|
||||
|
||||
### Katalogowanie — trzy filary metodologii
|
||||
|
||||
**1. Ustandaryzowany szablon opisu** — każdy wzorzec opisany wg tego samego formatu:
|
||||
- **Nazwa** — jedno słowo/fraza: „Layered", „Observer"
|
||||
- **Problem/Kontekst** — kiedy stosować
|
||||
- **Siły (forces)** — konkurencyjne wymagania do pogodzenia
|
||||
- **Rozwiązanie** — struktura, diagram, zachowanie
|
||||
- **Konsekwencje** — tradeoffs: co zyskujemy, co tracimy
|
||||
- **Powiązane wzorce** — jakie wzorce współgrają lub konkurują
|
||||
- **Znane zastosowania** — real-world examples
|
||||
Pytanie „JAK są katalogowane?" = jaką METODĘ stosujemy, żeby z setek wzorców zrobić przeszukiwalny, porównywalny, kompozytowalny system wiedzy. Odpowiedź: trzy filary, razem tworzące kompletną metodologię.
|
||||
|
||||
**2. Klasyfikacja wieloosiowa** — wzorce organizowane wzdłuż kilku osi jednocześnie:
|
||||

|
||||
|
||||
**1. Ustandaryzowany szablon opisu (pattern template)** — każdy wzorzec opisany wg tego samego formatu, dzięki czemu można je porównywać „pole po polu". Mnemonik: **NaPSiRoKo**.
|
||||
|
||||
| Pole | Skrót | Co zawiera | Przykład (Observer, GoF) |
|
||||
|------|-------|------------|--------------------------|
|
||||
| **Nazwa** | **Na** | jedno słowo/fraza | Observer |
|
||||
| **Problem** | **P** | kiedy stosować? | Obiekt zmienia stan → wielu zależnych musi zareagować, ale nie chcemy ich hard-codować |
|
||||
| **Siły** | **Si** | konkurencyjne wymagania | loose coupling vs koszt powiadomień (100 obserwatorów = 100 wywołań) |
|
||||
| **Rozwiązanie** | **Ro** | struktura + zachowanie | Subject trzyma listę Observer; przy zmianie woła notify() na każdym |
|
||||
| **Konsekwencje** | **Ko** | tradeoffs +/− | (+) luźne wiązanie, (−) kaskada powiadomień, memory leaks jeśli nie odrejestrujemy |
|
||||
| Powiązane | — | wzorce pokrewne | Mediator (centralizuje), Pub/Sub (rozproszony wariant) |
|
||||
| Znane zastosowania | — | real-world | Java Swing listeners, C# events, React useState → re-render |
|
||||
|
||||

|
||||
|
||||
**2. Klasyfikacja wieloosiowa** — wzorce organizowane wzdłuż kilku osi jednocześnie, jak książki w bibliotece (dział + półka + autor).
|
||||
|
||||
Osie klasyfikacji:
|
||||
- **Skala**: architektoniczny (cały system) → projektowy (klasa) → idiomatyczny (linia kodu)
|
||||
- **Domena problemu**: kreacyjne / strukturalne / behawioralne (GoF) albo warstwy / komunikacja / dekompozycja (POSA)
|
||||
- **Atrybut jakościowy**: wydajność, skalowalność, testowalność, dostępność
|
||||
|
||||
**3. Język wzorców (pattern language)** — wzorce referują się wzajemnie, tworząc graf:
|
||||
- Microservices → wymaga → API Gateway, Service Discovery, Circuit Breaker
|
||||
- Observer → wariant architektoniczny → Event-Driven Architecture
|
||||
- Nawigacja: „mam problem X → wzorzec A → prowadzi do problemu Y → wzorzec B"
|
||||
Konkretny przykład — jak GoF klasyfikuje 23 wzorce na dwóch osiach:
|
||||
|
||||
**Konkretne katalogi:**
|
||||
- **POSA** (1996) — wzorce architektoniczne: Layers, Pipes & Filters, Broker, MVC, Microkernel
|
||||
- **GoF** (1994) — 23 wzorce projektowe: kreacyjne (5), strukturalne (7), behawioralne (11)
|
||||
- **EIP** (2003) — wzorce integracji: Message Channel, Router, Aggregator
|
||||
- **PoEAA** (2002) — enterprise: Repository, Unit of Work, Domain Model, Active Record
|
||||
- **Cloud Patterns** (~2015) — chmurowe: Circuit Breaker, Sidecar, Saga, Strangler Fig
|
||||
| | Kreacyjne (5) | Strukturalne (7) | Behawioralne (11) |
|
||||
|---|---|---|---|
|
||||
| **Klasa** | Factory Method | Adapter (class) | Interpreter, Template Method |
|
||||
| **Obiekt** | Abstract Factory, Builder, Prototype, Singleton | Adapter (obj), Bridge, Composite, Decorator, Facade, Flyweight, Proxy | Chain of Resp., Command, Iterator, Mediator, Memento, **Observer**, State, Strategy, Visitor |
|
||||
|
||||
Observer jest w komórce: **behawioralny × obiekt**. Wiedzieć GDZIE wzorzec leży = szybsze przypomnienie i porównanie z sąsiadami (Mediator, State, Strategy — też behawioralne obiektowe).
|
||||
|
||||

|
||||
|
||||
**3. Język wzorców (pattern language)** — wzorce referują się wzajemnie, tworząc nawigacyjny graf „zobacz też". Sens: masz problem → stosujesz wzorzec A → A rodzi nowy problem → wzorzec B go rozwiązuje.
|
||||
|
||||
Konkretna nawigacja w praktyce:
|
||||
|
||||
Problem: „monolith nie skaluje się"
|
||||
↓
|
||||
Wzorzec: Microservices
|
||||
↓ wymaga
|
||||
Problem: „jak routować żądania do serwisów?"
|
||||
↓
|
||||
Wzorzec: API Gateway
|
||||
↓ rodzi problem
|
||||
Problem: „co gdy serwis nie odpowiada?"
|
||||
↓
|
||||
Wzorzec: Circuit Breaker
|
||||
↓ rodzi problem
|
||||
Problem: „jak zachować spójność transakcji?"
|
||||
↓
|
||||
Wzorzec: Saga
|
||||
|
||||
Każdy wzorzec w katalogu ma pole „Powiązane wzorce" — to linki w tym grafie.
|
||||
|
||||

|
||||
|
||||
**Konkretne katalogi** (5 głównych — mnemonik **PGEP+C** = „Paweł Grał Efektownie Pod Chmurami"):
|
||||
|
||||
| Katalog | Rok | Autorzy | Skala | Domena | Przykładowe wzorce |
|
||||
|---------|-----|---------|-------|--------|--------------------|
|
||||
| **POSA** | 1996 | Buschmann et al. | architektoniczny | systemy | Layers, Pipes & Filters, Broker, MVC, Microkernel |
|
||||
| **GoF** | 1994 | Gamma, Helm, Johnson, Vlissides | projektowy | obiekty | Factory, Singleton, Observer, Strategy (23 łącznie) |
|
||||
| **EIP** | 2003 | Hohpe & Woolf | integracyjny | komunikacja między-systemowa | Message Channel, Router, Aggregator |
|
||||
| **PoEAA** | 2002 | Martin Fowler | projektowy/arch. | enterprise | Repository, Unit of Work, Domain Model, Active Record |
|
||||
| **Cloud** | ~2015 | Microsoft/AWS | architektoniczny | chmura | Circuit Breaker, Sidecar, Saga, Strangler Fig |
|
||||
|
||||
### Przykładowe wzorce
|
||||
|
||||
@ -136,16 +180,43 @@
|
||||
|
||||
### Jak zapamiętać
|
||||
|
||||
- **Mnemonik katalogów „PGEP+C"**: **P**OSA → **G**oF → **E**IP → **P**oEAA + **C**loud
|
||||
- Historia: „**P**aweł **G**rał **E**fektownie **P**od **C**hmurami"
|
||||
- Chronologicznie: GoF '94 → POSA '96 → PoEAA '02 → EIP '03 → Cloud ~'15
|
||||
- **Szablon wzorca „NaPSiRoKo"**: **Na**zwa, **P**roblem, **Si**ły, **Ro**związanie, **Ko**nsekwencje
|
||||
- Wyobraź sobie kartonowe pudełko: etykieta (Nazwa) → co nie działa (Problem) → wagi na szalce (Siły) → instrukcja montażu (Rozwiązanie) → lista „+" i „−" na boku (Konsekwencje)
|
||||
- **3 filary katalogowania**: Szablon + Klasyfikacja + Język wzorców
|
||||
- Analogia do encyklopedii: każde hasło ma ten sam format (szablon), jest w kategorii z innymi hasłami tego typu (klasyfikacja), i ma „zobacz też" (język wzorców)
|
||||
- **„Monolith first"** — rozdzielaj gdy znasz granice domen
|
||||
- **Wzorzec = Nazwa + Problem + Rozwiązanie + Konsekwencje** (minimum do zapamiętania z dowolnego katalogu)
|
||||
**Mnemonik 1 — szablon wzorca „NaPSiRoKo":**
|
||||
- **Na**zwa → **P**roblem → **Si**ły → **Ro**związanie → **Ko**nsekwencje
|
||||
- Historyjka: „**Na**pisałem **P**roblem na kartce, **Si**ły mnie ciągnęły w dwie strony, **Ro**związałem go, a **Ko**nsekwencje spisałem na odwrocie"
|
||||
- Wyobraź sobie kartonowe pudełko: etykieta (Nazwa) → co nie działa (Problem) → wagi na szalce (Siły) → instrukcja montażu (Rozwiązanie) → lista „+" i „−" na boku (Konsekwencje)
|
||||
|
||||
**Mnemonik 2 — katalogi „PGEP+C" = „Paweł Grał Efektownie Pod Chmurami":**
|
||||
|
||||
P = POSA (1996, systemy) „Paweł"
|
||||
G = GoF (1994, obiekty) „Grał"
|
||||
E = EIP (2003, integracja) „Efektownie"
|
||||
P = PoEAA (2002, enterprise) „Pod"
|
||||
C = Cloud (~2015, chmura) „Chmurami"
|
||||
|
||||
- Chronologicznie: GoF '94 → POSA '96 → PoEAA '02 → EIP '03 → Cloud ~'15
|
||||
- Skala rośnie: GoF (obiekty) → PoEAA (aplikacja) → POSA/EIP (system) → Cloud (infrastruktura)
|
||||
|
||||
**Mnemonik 3 — trzy filary katalogowania „SzKlaJ" = „Szklany Jar":**
|
||||
- **Sz**ablon opisu (NaPSiRoKo) — każde hasło w tym samym formacie
|
||||
- **Kla**syfikacja wieloosiowa — hasła posortowane w kategorie (jak dział w bibliotece)
|
||||
- **J**ęzyk wzorców — hasła mają „zobacz też" (graf nawigacyjny)
|
||||
- Analogia: encyklopedia. Każde hasło ma ten sam format (**Sz**ablon), jest w kategorii z innymi hasłami tego typu (**Kla**syfikacja), i ma „zobacz też" (**J**ęzyk wzorców)
|
||||
|
||||
**Mnemonik 4 — GoF 3 kategorie „KSB" = „Kto Stworzył Budynek?":**
|
||||
- **K**reacyjne (5) — JAK tworzyć obiekty? (Factory, Singleton, Builder, Prototype, Abstract Factory)
|
||||
- **S**trukturalne (7) — JAK składać obiekty? (Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy)
|
||||
- **B**ehawioralne (11) — JAK obiekty komunikują? (Observer, Strategy, Command, State, Iterator...)
|
||||
- Zapamiętaj liczby: 5 + 7 + 11 = 23
|
||||
|
||||
**Szybka ściąga — wzorzec na obronie:**
|
||||
- Wzorzec = Nazwa + Problem + Rozwiązanie + Konsekwencje (minimum do zapamiętania z dowolnego katalogu)
|
||||
- „Monolith first" — rozdzielaj gdy znasz granice domen
|
||||
- Katalogi wg skali: POSA = systemy, GoF = obiekty, EIP = komunikacja międzysystemowa
|
||||
|
||||
→ Diagramy do druku: `pytania/img/q14_pattern_template.png`, `pytania/img/q14_catalog_map.png`
|
||||
→ Diagramy do druku:
|
||||
- `pytania/img/q14_pattern_template.png` — szablon NaPSiRoKo
|
||||
- `pytania/img/q14_catalog_map.png` — mapa katalogów PGEP+C
|
||||
- `pytania/img/q14_three_pillars.png` — trzy filary katalogowania
|
||||
- `pytania/img/q14_observer_card_filled.png` — wypełniona karta wzorca Observer
|
||||
- `pytania/img/q14_pattern_language_navigation.png` — nawigacja w języku wzorców
|
||||
|
||||
|
||||
@ -12,6 +12,261 @@
|
||||
|
||||
---
|
||||
|
||||
**Robot przemysłowy (industrial robot)** — manipulator: ramię mechaniczne z 4–7 osiami obrotu (degrees of freedom), zamocowane na stałe, sterowane komputerowo. Typowe zastosowania: spawanie, malowanie, paletyzacja, montaż. Standard ISO 8373: „automatycznie sterowany, reprogramowalny, wielozadaniowy manipulator". Przykłady: ABB IRB 6700 (spawanie karoserii), KUKA KR 210 (paletyzacja), FANUC M-20iA (montaż elektroniki). W odróżnieniu od cobotów (collaborative robots), roboty przemysłowe pracują w klatkach bezpieczeństwa — nie wolno wchodzić w ich zasięg podczas pracy.
|
||||
|
||||
Robot przemysłowy — budowa:
|
||||
[Podstawa] → [Oś 1: obrót] → [Oś 2: ramię] → [Oś 3: łokieć]
|
||||
→ [Oś 4: nadgarstek] → [Oś 5: pochylenie] → [Oś 6: obrót TCP]
|
||||
↓
|
||||
[Narzędzie/chwytak]
|
||||
|
||||
---
|
||||
|
||||
**Język ogólnego przeznaczenia (general-purpose language)** — język programowania zaprojektowany do rozwiązywania DOWOLNYCH problemów: aplikacje webowe, bazy danych, gry, systemy operacyjne, AI. Przykłady: C++, Python, Java, C#, Rust. Nie ma wbudowanych komend ruchu robota — trzeba pisać biblioteki lub sterowniki od zera. W kontekście robotyki: C++ i Python używane z frameworkami (ROS, MoveIt), ale NIE są to języki specjalizowane.
|
||||
|
||||
// C++ (ogólny) — żeby ruszyć robotem, musisz:
|
||||
robot.setJointAngle(0, 45.0); // sam zarządzasz komunikacją
|
||||
robot.setJointAngle(1, 30.0); // sam pilnujesz kinematyki
|
||||
robot.sendCommand(); // sam wysyłasz pakiety
|
||||
|
||||
// RAPID (specjalizowany) — komenda ruchu to prymityw języka:
|
||||
MoveL pTarget, v500, fine, tool1; // gotowe, 1 linia
|
||||
|
||||
**Składnia (syntax)** — reguły, JAK pisać poprawne polecenia w danym języku. Składnia określa: jakie słowa kluczowe istnieją, jak je łączyć, jak kończyć instrukcje, jak grupować bloki kodu. Analogia: składnia to gramatyka języka naturalnego — „Ala ma kota" jest poprawne, „kota Ala ma" jest zrozumiałe, ale „ma Ala kota" w programowaniu dałoby błąd.
|
||||
|
||||
Różne składnie — ta sama instrukcja „jeśli x > 5, zrób coś":
|
||||
C-like: if (x > 5) { doSomething(); }
|
||||
Pascal-like: IF x > 5 THEN doSomething; ENDIF
|
||||
Python-like: if x > 5:\n do_something()
|
||||
|
||||
**Typy danych (data types)** — kategorie wartości, jakie język rozpoznaje. Typ mówi kompilatorowi/interpreterowi ile pamięci zająć i jakie operacje są dozwolone. Języki robotów mają UNIKALNE typy, których nie znajdziesz w C++ czy Pythonie.
|
||||
|
||||
Typy ogólne (istnieją w każdym języku):
|
||||
INT / num → liczba całkowita: 42, -7, 0
|
||||
REAL / float → liczba zmiennoprzecinkowa: 3.14, -0.001
|
||||
BOOL → prawda/fałsz: TRUE, FALSE
|
||||
STRING → tekst: "Hello"
|
||||
|
||||
Typy SPECYFICZNE dla robotyki (nie istnieją w C++/Python):
|
||||
robtarget → pozycja + orientacja + konfiguracja ramienia (RAPID)
|
||||
E6POS → x,y,z + a,b,c (kąty Eulera) + osie zewnętrzne (KRL)
|
||||
POSITION → pozycja kartezjańska (Karel)
|
||||
pose → [x, y, z, rx, ry, rz] (URScript)
|
||||
tooldata → definicja narzędzia (TCP + masa + środek ciężkości)
|
||||
speeddata → prędkość TCP + prędkość obrotowa
|
||||
zonedata → strefa zbliżenia (jak blisko celu jechać)
|
||||
|
||||
**Instrukcja (instruction/statement)** — pojedyncze polecenie w programie. Komputer/robot wykonuje instrukcje po kolei (sekwencyjnie). W językach robotów instrukcje dzielą się na:
|
||||
|
||||
1. Ruchu: MoveL pTarget, v500, fine, tool1;
|
||||
2. I/O: SetDO doGripper, 1;
|
||||
3. Czekania: WaitTime 0.5;
|
||||
4. Kontroli: IF sensor = TRUE THEN ...
|
||||
5. Przypisania: nCycles := nCycles + 1;
|
||||
6. Wywołania: PickPart; (wywołanie procedury)
|
||||
|
||||
---
|
||||
|
||||
**Zadanie robotyczne (robotic task)** — czynność, którą robot ma wykonać w ramach procesu produkcyjnego. Składa się z sekwencji ruchów, operacji I/O i logiki. Przykłady:
|
||||
- Pick & place: podnieś obiekt z punktu A, przenieś do punktu B
|
||||
- Spawanie: jedź po ścieżce spoiny z włączonym łukiem
|
||||
- Paletyzacja: układaj pudła na palecie w warstwy 4×3
|
||||
- Montaż: włóż kołek w otwór z kontrolą siły
|
||||
|
||||
**Ruch robota (robot motion)** — zmiana pozycji ramienia robota w czasie. Każdy ruch jest zdefiniowany przez cztery elementy: CEL (dokąd), TYP (jak — liniowo, stawowo, po łuku), PRĘDKOŚĆ (jak szybko), PRECYZJA (strefa zbliżenia). W językach robotów ruch to PRYMITYW — jedno polecenie, nie pętla obliczeń.
|
||||
|
||||
Trzy typy ruchu — jak robot jedzie z A do B:
|
||||
|
||||
PTP / MoveJ (joint):
|
||||
A ●─ ─ ─ ── ● B ← ścieżka TCP nieprzewidywalna (krzywa)
|
||||
Ale najszybszy! Interpolacja w przestrzeni stawów.
|
||||
|
||||
LIN / MoveL (linear):
|
||||
A ●──────────● B ← TCP jedzie po prostej linii
|
||||
Wolniejszy, ale precyzyjna ścieżka. Wymaga ciągłego IK.
|
||||
|
||||
CIRC / MoveC (circular):
|
||||
A ●╲ ╱● B ← TCP jedzie po łuku kołowym
|
||||
╲ ● H ╱ H = punkt pomocniczy (definiuje łuk)
|
||||
╲─────╱
|
||||
|
||||
**Definiowanie ruchów** — w językach specjalizowanych ruch definiujesz JEDNĄ instrukcją z parametrami. Nie musisz pisać pętli sterowania silnikami — język ukrywa kinematykę odwrotną, planowanie trajektorii i sterowanie serwomechanizmami.
|
||||
|
||||
MoveL pTarget, v500, z10, tGripper;
|
||||
│ │ │ │ └── narzędzie (jaki TCP)
|
||||
│ │ │ └── precyzja (strefa zbliżenia)
|
||||
│ │ └── prędkość (500 mm/s)
|
||||
│ └── cel (pozycja docelowa)
|
||||
└── typ ruchu (liniowy)
|
||||
|
||||
---
|
||||
|
||||
**I/O cyfrowe i analogowe (digital/analog I/O)** — interfejsy wejścia/wyjścia robota do komunikacji z urządzeniami zewnętrznymi (chwytaki, czujniki, przenośniki, lampy sygnalizacyjne).
|
||||
|
||||
- **I/O cyfrowe (digital)** — sygnał ma dwa stany: 0 (OFF) lub 1 (ON). Jak wyłącznik światła. Użycie: włącz/wyłącz chwytak, sprawdź czy czujnik wykrył obiekt, sygnalizuj gotowość do PLC.
|
||||
|
||||
- **I/O analogowe (analog)** — sygnał ciągły w zakresie, np. 0–10V lub 4–20mA. Jak regulator głośności. Użycie: ustaw siłę chwytaka proporcjonalnie (nie tylko ON/OFF), odczytaj temperaturę z termopary, ustaw prędkość przenośnika.
|
||||
|
||||
I/O cyfrowe — jak włącznik (ON/OFF):
|
||||
SetDO doGripper, 1; // RAPID: włącz chwytak (ON)
|
||||
SetDO doGripper, 0; // RAPID: wyłącz chwytak (OFF)
|
||||
OUT 1 TRUE // KRL: włącz wyjście 1
|
||||
DOUT[1] = ON // Karel: włącz wyjście 1
|
||||
set_digital_out(0, True) // URScript
|
||||
|
||||
I/O analogowe — jak potencjometr (wartość ciągła):
|
||||
SetAO aoForce, 5.7; // RAPID: ustaw wyjście analogowe na 5.7V
|
||||
// np. siła chwytaka proporcjonalna do napięcia:
|
||||
// 0V = brak siły, 10V = maksymalna siła
|
||||
|
||||
---
|
||||
|
||||
**Pozycja (position)** — punkt w przestrzeni, do którego robot ma dojechać. Opisywana trzema współrzędnymi: x, y, z (odległości w milimetrach od początku układu współrzędnych). Sama pozycja to za mało — robot musi też wiedzieć, JAK narzędzie ma być obrócone w tym punkcie (orientacja).
|
||||
|
||||
Pozycja: (x=400, y=200, z=100)
|
||||
Znaczenie: 400mm do przodu, 200mm w prawo, 100mm w górę
|
||||
od początku układu współrzędnych robota (podstawa)
|
||||
|
||||
**Układ kartezjański (Cartesian coordinate system)** — układ współrzędnych xyz, w którym każdy punkt w przestrzeni jest opisany trzema prostopadłymi odległościami od początku. Nazwa od René Descartesa (Kartezjusz). W robotyce: pozycja TCP wyrażona w mm: x=400, y=200, z=100. Alternatywa: przestrzeń stawowa (kąty: q₁=30°, q₂=45°, q₃=-10°…), która opisuje tę samą pozycję z perspektywy silników.
|
||||
|
||||
Układ kartezjański:
|
||||
Z ↑
|
||||
│ ● TCP (400, 200, 100)
|
||||
│ ╱
|
||||
│ ╱
|
||||
│ ╱
|
||||
├──────→ Y
|
||||
╱
|
||||
╱
|
||||
X
|
||||
|
||||
Przestrzeń stawowa (ta sama pozycja, ale w kątach):
|
||||
q₁ = 26.6°, q₂ = 45.0°, q₃ = -12.3°, q₄ = 0°, q₅ = 57.3°, q₆ = 0°
|
||||
|
||||
**Orientacja (orientation)** — kierunek, w jakim narzędzie (TCP) jest skierowane w danym punkcie. Pozycja mówi GDZIE jest, orientacja mówi JAK jest obrócone. Wyrażana jako kwaternion (q1,q2,q3,q4) w RAPID lub kąty Eulera (A,B,C) w KRL.
|
||||
|
||||
Ta sama pozycja, różne orientacje:
|
||||
Orientacja 1: chwytak skierowany w dół ↓ (spawanie poziomej płyty)
|
||||
Orientacja 2: chwytak skierowany w bok → (wkładanie elementu w otwór)
|
||||
Orientacja 3: chwytak skierowany w górę ↑ (podpieranie od spodu)
|
||||
|
||||
W RAPID (kwaternion): [1, 0, 0, 0] = narzędzie pionowo w dół
|
||||
W KRL (kąty Eulera): A=0, B=0, C=180 = obrót 180° wokół Z
|
||||
|
||||
**Konfiguracja ramienia (robot configuration)** — dla jednej pozycji + orientacji robot 6-osiowy może mieć do 8 różnych ustawień stawów (jak zgięcie łokcia do góry lub do dołu). Konfiguracja to dodatkowy parametr, który mówi robotowi KTÓRE rozwiązanie kinematyki odwrotnej wybrać.
|
||||
|
||||
Ta sama pozycja, dwie konfiguracje:
|
||||
|
||||
Konfiguracja 1 ("łokieć do góry"): Konfiguracja 2 ("łokieć na dół"):
|
||||
╱──╲ ╲──╱
|
||||
╱ ╲ ╲ ╲
|
||||
───╱ ● TCP ───╱ ● TCP
|
||||
|
||||
W RAPID: robtarget = [[x,y,z], [orientacja], [konfiguracja], [osie_zewn]]
|
||||
Konfiguracja = [cf1, cf4, cf6, cfx] — opisuje kwadranty stawów
|
||||
|
||||
---
|
||||
|
||||
**Język strukturalny (structured language)** — język z jasno zdefiniowanymi blokami kodu: procedury (PROC/ENDPROC), pętle (WHILE/ENDWHILE, FOR/ENDFOR), warunki (IF/ELSE/ENDIF). Przeciwieństwo: kod spaghetti z GOTO i nienazwanymi skokami. RAPID jest strukturalny — program składa się z modułów (MODULE/ENDMODULE) zawierających procedury. Łatwy do czytania i utrzymania.
|
||||
|
||||
Strukturalność RAPID:
|
||||
MODULE MainModule ← moduł (pojemnik)
|
||||
PROC PickPart() ← procedura (nazwany blok)
|
||||
IF ready THEN ← warunek
|
||||
MoveL ...;
|
||||
ENDIF
|
||||
ENDPROC ← wyraźny koniec bloku
|
||||
ENDMODULE
|
||||
|
||||
**Język wielozadaniowy (multitasking language)** — język umożliwiający uruchamianie wielu programów (tasków) jednocześnie na jednym kontrolerze. W RAPID: jeden task steruje ruchem ramienia, drugi monitoruje czujniki bezpieczeństwa, trzeci komunikuje się z PLC. Każdy task działa „równolegle" (w rzeczywistości: szybkie przełączanie kontekstu).
|
||||
|
||||
RAPID — wielozadaniowość:
|
||||
Task 1 (MAIN): MoveL → MoveL → MoveL... (sterowanie ruchem)
|
||||
Task 2 (SAFETY): WHILE TRUE DO (ciągły monitoring)
|
||||
IF DI(emergency) THEN Stop;
|
||||
ENDWHILE
|
||||
Task 3 (COMM): czytaj/wysyłaj dane do PLC (komunikacja)
|
||||
← kontroler przełącza się między taskami co ~4ms
|
||||
|
||||
---
|
||||
|
||||
**Język Pascal-like** — język, którego składnia przypomina język Pascal (Niklaus Wirth, 1970). Cechy: bloki zaczynają się słowem kluczowym i kończą `END` (nie nawiasami `{}`), zmienne deklarowane na początku bloku (`VAR`/`DECL`), średniki jako separatory, brak rozróżniania wielkości liter (case-insensitive). KRL i Karel mają składnię Pascal-like.
|
||||
|
||||
Pascal: KRL (KUKA):
|
||||
PROGRAM example; DEF PickAndPlace()
|
||||
VAR x: INTEGER; DECL INT x
|
||||
BEGIN ; (brak BEGIN/END w KRL,
|
||||
x := 10; ; ale DEF/END pełni tę rolę)
|
||||
IF x > 5 THEN IF x > 5 THEN
|
||||
WriteLn('tak'); ; zrób coś
|
||||
END; ENDIF
|
||||
END. END
|
||||
|
||||
**Approximacja (approximation)** — termin KUKA odpowiadający „strefie zbliżenia" w RAPID. Parametr `$APO.CDIS = 10` oznacza: robot zaczyna skręcać w stronę następnego celu, gdy jest 10mm od bieżącego. Efekt identyczny jak `z10` w RAPID — płynniejszy ruch, szybszy cykl, ale TCP nie dochodzi dokładnie do zaprogramowanego punktu.
|
||||
|
||||
RAPID: MoveL p1, v500, z10, tool1; // strefa = 10mm
|
||||
KRL: $APO.CDIS = 10 // approximacja = 10mm
|
||||
LIN XTarget C_DIS // C_DIS = zastosuj approximację
|
||||
|
||||
---
|
||||
|
||||
**Język Python-like** — składnia inspirowana Pythonem: brak nawiasów klamrowych `{}`, proste wywołania funkcji, brak deklaracji typów, czytelny kod przypominający pseudokod. URScript jest Python-like — ale UWAGA: wcięcia w URScript NIE definiują bloków (w odróżnieniu od Pythona). Bloki kończą się słowem `end`.
|
||||
|
||||
Python: URScript:
|
||||
def pick(): def pick():
|
||||
target = [0.4, 0.2] target = p[0.4, 0.2, 0.1, 3.14, 0, 0]
|
||||
move(target) movel(target, a=0.5, v=0.3)
|
||||
end ← URScript wymaga 'end' (Python nie)
|
||||
|
||||
**Język skryptowy (scripting language)** — język interpretowany (nie kompilowany): program czytany i wykonywany linia po linii w trakcie działania, bez osobnego kroku kompilacji. Zalety: szybkie testowanie (zmień kod → uruchom od razu), brak czekania na kompilację. Wady: wolniejszy od skompilowanego kodu (ale dla robota to nie problem — wąskim gardłem jest fizyczny ruch, nie szybkość interpretera). URScript jest skryptowy.
|
||||
|
||||
Kompilowany (C++, Karel): Skryptowy (URScript, Python):
|
||||
1. Napisz kod 1. Napisz kod
|
||||
2. Skompiluj (czekaj...) 2. Uruchom natychmiast
|
||||
3. Przenieś na kontroler ← brak kroku kompilacji
|
||||
4. Uruchom
|
||||
|
||||
**Typowanie dynamiczne (dynamic typing)** — zmiennej NIE deklarujesz typu — język sam go rozpoznaje w momencie przypisania. Zmienna może zmieniać typ w trakcie programu. Przeciwieństwo: typowanie statyczne (C++, KRL) — typ deklarujesz z góry i nie może się zmienić.
|
||||
|
||||
Dynamiczne (URScript/Python): Statyczne (KRL):
|
||||
x = 42 ← x jest liczbą DECL INT x ← x musi być INT
|
||||
x = "hello" ← teraz x tekst x = 42 ← OK
|
||||
← BRAK błędu x = "hello" ← BŁĄD kompilacji!
|
||||
|
||||
**Niski próg wejścia (low barrier to entry)** — URScript jest łatwy do nauki, bo łączy cechy ułatwiające start:
|
||||
1. Brak deklaracji typów — nie musisz znać `INT`, `REAL`, `E6POS`
|
||||
2. Składnia Python-like — jeśli znasz Pythona, czytasz URScript od razu
|
||||
3. Prosty model: `movel(cel, prędkość)` — intuicyjne wywołanie
|
||||
4. Coboty adresowane do małych firm bez zespołu programistów
|
||||
5. Darmowy symulator URSim — uczysz się bez kupowania robota
|
||||
6. Polyscope (GUI) — operator może programować drag & drop
|
||||
|
||||
Krzywa uczenia się (orientacyjnie):
|
||||
URScript: dni (Python-like, prosty)
|
||||
RAPID: tygodnie (strukturalny, wiele typów danych)
|
||||
KRL: tygodnie (Pascal-like, dwa pliki)
|
||||
Karel/TP: dni (TP) / tygodnie (Karel pełny)
|
||||
ROS+C++: miesiące (framework + język ogólny + Linux)
|
||||
|
||||
---
|
||||
|
||||
**Język proceduralny (procedural language)** — paradygmat programowania, w którym program to sekwencja PROCEDUR (funkcji) wywoływanych po kolei. Każda procedura wykonuje konkretne zadanie. Brak obiektów i klas (to byłby język obiektowy). Wszystkie języki robotów są proceduralne — program to lista kroków: jedź tu, zamknij chwytak, jedź tam, otwórz chwytak.
|
||||
|
||||
Proceduralny = lista kroków: Obiektowy = obiekty i metody:
|
||||
PickPart(); robot.pick(objectA);
|
||||
MoveTo(placePos); objectA.moveTo(placePos);
|
||||
OpenGripper(); gripper.open();
|
||||
|
||||
**Język C-like** — składnia inspirowana językiem C: nawiasy klamrowe `{}` lub `:=` do przypisań, średniki na końcu instrukcji, zmienne ze znakiem `$` dla zmiennych systemowych. PDL2 (Comau) jest C-like: `$DOUT[1] := TRUE` przypomina składnię C z operatorem przypisania.
|
||||
|
||||
C: PDL2 (Comau):
|
||||
int x = 10; VAR x : INTEGER
|
||||
if (x > 5) { IF x > 5 THEN
|
||||
output[1] = 1; $DOUT[1] := TRUE
|
||||
} ENDIF
|
||||
|
||||
---
|
||||
|
||||
**Poziomy abstrakcji T-R-M-S:**
|
||||
|
||||
**Task-level (poziom zadania)** — najwyższy: opisujesz CO robot ma zrobić, nie JAK. „Podnieś A, połóż na B." Robot sam planuje ruchy. Przykłady: PDDL, Behavior Trees.
|
||||
@ -74,6 +329,41 @@
|
||||
|
||||
---
|
||||
|
||||
### Specjalizowane języki programowania robotów — przegląd
|
||||
|
||||
Specjalizowane języki programowania robotów to języki stworzone wyłącznie do sterowania robotami przemysłowymi. Nie są językami ogólnego przeznaczenia (jak C++ czy Python) — ich składnia, typy danych i instrukcje są zaprojektowane wokół zadań robotycznych: definiowania ruchów, obsługi I/O cyfrowych i analogowych, zarządzania narzędziami (TCP) i reagowania na błędy w czasie rzeczywistym.
|
||||
|
||||
**Główne specjalizowane języki producentów (robot-level):**
|
||||
|
||||
1. **RAPID (ABB)** — strukturalny, wielozadaniowy. Komendy ruchu: `MoveL`, `MoveJ`, `MoveC`. Pozycje opisywane typem `robtarget` (kartezjańska + orientacja + konfiguracja). Dwupoziomowy parametr ruchu: prędkość (`v500` = 500 mm/s) + strefa zbliżenia (`z10`, `fine`). Obsługuje wielowątkowość (wiele tasków). Symulator: RobotStudio.
|
||||
|
||||
2. **KRL — KUKA Robot Language** — Pascal-like (`DEF/END`, `DECL`). Program = dwa pliki: `.src` (kod) + `.dat` (pozycje). Komendy ruchu: `PTP`, `LIN`, `CIRC`. Prędkość ustawiana zmienną systemową `$VEL.CP`. Approximacja (`C_DIS`) zamiast stref. Symulator: KUKA.Sim Pro.
|
||||
|
||||
3. **Karel (FANUC)** — Pascal-like (`PROGRAM/BEGIN/END`). Dwa tryby: Karel (pełny tekstowy) i TP (Teach Pendant — uproszczony, listowy, numerowane linie). W praktyce fabrycznej dominuje TP — operatorzy bez wykształcenia programistycznego uczą się numerowanych linii `L P[2] 500mm/sec FINE`. Symulator: ROBOGUIDE.
|
||||
|
||||
4. **URScript (Universal Robots)** — Python-like, skryptowy, dynamicznie typowany. Zaprojektowany dla cobotów (robotów współpracujących). Unikalne: `force_mode()` (sterowanie siłą), `freedrive_mode()` (ręczne prowadzenie). Niski próg wejścia. Symulator: URSim (darmowy).
|
||||
|
||||
5. **PDL2 (Comau)** — proceduralny, C-like. `MOVE LINEAR TO`, `$DOUT[1] := TRUE`. Stosowany głównie w automotive (Fiat/Stellantis). Symulator: RoboSim.
|
||||
|
||||
**Języki task-level (planowanie):**
|
||||
|
||||
6. **PDDL (Planning Domain Definition Language)** — deklaratywny język opisu problemów planowania. Definiujesz stany, akcje (warunki + efekty) i cel — planner automatycznie znajduje sekwencję akcji. Nie steruje robotem bezpośrednio, lecz generuje plan, który robot-level realizuje.
|
||||
|
||||
7. **Behavior Trees** — struktury drzew zachowań (Sequence, Selector, Action, Condition). Stosowane w robotyce i grach. Alternatywa dla maszyn stanów — łatwiejsze w rozbudowie i debugowaniu.
|
||||
|
||||
**Middleware i frameworki (uniwersalne, nie jednego producenta):**
|
||||
|
||||
8. **ROS / ROS 2** — middleware publish/subscribe, programowanie w Python/C++. NIE jest językiem specjalizowanym per se, ale jest de facto standardem łączącym roboty wielu producentów. Biblioteka **MoveIt** (motion planning) przełamuje vendor lock-in.
|
||||
|
||||
9. **Orocos** — framework C++ do hard real-time sterowania (<1 ms). Wypełnia lukę ROS w pętlach regulacji wymagających gwarancji czasowych.
|
||||
|
||||
**Wspólne cechy języków specjalizowanych:**
|
||||
- Wbudowane typy pozycji (kartezjańska, stawowa) — nie istnieją w C++ czy Pythonie
|
||||
- Komendy ruchu jako prymitywy języka (`MoveL`, `LIN`, `movel`) — nie wywołania bibliotek
|
||||
- Obsługa I/O cyfrowego/analogowego jako element składni
|
||||
- Parametry ruchu (prędkość, strefa zbliżenia, narzędzie) jako argumenty instrukcji
|
||||
- Brak wskaźników, zarządzania pamięcią, struktur danych ogólnego przeznaczenia — język zoptymalizowany pod jedno zastosowanie
|
||||
|
||||
### Klasyfikacja wg poziomu abstrakcji: **T-R-M-S**
|
||||
|
||||
1. **Task-level** — „Podnieś A, połóż na B" (PDDL, Behavior Trees)
|
||||
|
||||
@ -59,6 +59,42 @@
|
||||
|
||||
---
|
||||
|
||||
#### Pojęcia kluczowe dla progowania i Otsu
|
||||
|
||||
**Wariancja (variance, σ²)** — miara tego, jak bardzo wartości RÓŻNIĄ SIĘ od swojej średniej. Im większa wariancja, tym bardziej „rozrzucone" są dane. Wzór: σ² = Σ(xᵢ - μ)² / n, gdzie μ to średnia.
|
||||
|
||||
Przykład 1 — MAŁA wariancja (dane skupione):
|
||||
wartości: [48, 50, 52, 49, 51] średnia μ = 50
|
||||
σ² = ((48-50)² + (50-50)² + (52-50)² + (49-50)² + (51-50)²) / 5
|
||||
= (4 + 0 + 4 + 1 + 1) / 5 = 2.0
|
||||
|
||||
Przykład 2 — DUŻA wariancja (dane rozrzucone):
|
||||
wartości: [10, 90, 30, 80, 50] średnia μ = 52
|
||||
σ² = ((10-52)² + (90-52)² + (30-52)² + (80-52)² + (50-52)²) / 5
|
||||
= (1764 + 1444 + 484 + 784 + 4) / 5 = 896.0
|
||||
|
||||
Mała σ² = punkty blisko średniej = dane JEDNORODNE
|
||||
Duża σ² = punkty daleko od średniej = dane RÓŻNORODNE
|
||||
|
||||
**Wewnątrzklasowa (within-class)** — „wewnątrz klasy" oznacza, że mierzymy wariancję OSOBNO dla każdej grupy (klasy), a potem ważymy wynik proporcją pikseli w grupie. Jeśli klasa 0 ma piksele [30, 50, 45] a klasa 1 ma piksele [180, 200, 190], to σ²_wewnątrz = (udział_kl0 × σ²_kl0) + (udział_kl1 × σ²_kl1).
|
||||
|
||||
**Wariancja wewnątrzklasowa (within-class variance)** — obliczasz wariancję KAŻDEJ klasy osobno, ważysz przez udział pikseli w tej klasie, sumujesz. Jeśli σ²_wewnątrz jest MAŁA → klasy są „jednorodne" (piksele w klasie 0 mają podobne jasności, piksele w klasie 1 też).
|
||||
|
||||
**Co to znaczy „klasy jednorodne"?** — jednorodna klasa to taka, w której WSZYSTKIE piksele mają podobne wartości. Np. klasa „tło" ma jasności [195, 200, 198, 205] → jednorodna (σ² mała). Klasa mieszająca tło i obiekt [30, 200, 50, 190] → niejednorodna (σ² duża). Otsu szuka progu T, który daje NAJBARDZIEJ jednorodne klasy.
|
||||
|
||||
**Histogram bimodalny (bimodal histogram)** — histogram z DWOMA wyraźnymi „garbami" (pikami). „Bi" = dwa, „modal" = moda (najczęstsza wartość). Typowy dla obrazów z jednym obiektem na tle — garb 1 odpowiada ciemnym pikselom (obiekt), garb 2 jasnym (tło). Otsu działa TYLKO gdy histogram jest bimodalny — bo szuka progu MIĘDZY garbami.
|
||||
|
||||
Garb 1 (ciemne~60): piksele obiektu
|
||||
Garb 2 (jasne~190): piksele tła
|
||||
Dolina między garbami → tu Otsu stawia próg T!
|
||||
|
||||
Gdyby histogram miał JEDEN garb (unimodalny) → brak naturalnego
|
||||
podziału → Otsu wybierze losowy próg → słaby wynik.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
**Thresholding (progowanie)** — najprostsza metoda segmentacji. Pomysł: każdy piksel ma wartość jasności (0=czarny, 255=biały). Wybierz PRÓG T: piksel > T → klasa 1 (obiekt), piksel ≤ T → klasa 0 (tło). Działa lepiej niż się wydaje na prostych obrazach (tekst na kartce, RTG, dokumenty).
|
||||
|
||||
Obraz (jasność pikseli): [50][200][180][30][220][190]
|
||||
@ -71,16 +107,55 @@
|
||||
|
||||
Problem: JAK wybrać T? Ręcznie → subiektywne. Rozwiązanie → Otsu.
|
||||
|
||||
**Otsu** — automatyczny dobór progu. Algorytm: przetestuj WSZYSTKIE progi T=0..255, dla każdego oblicz wariancję wewnątrzklasową (jak „różnorodne" są piksele w klasie 0 i klasie 1). Wybierz T minimalizujące tę wariancję = klasy jak najbardziej jednorodne. Złożoność: O(n·L) gdzie n=piksele, L=poziomy jasności (256). Ograniczenie: działa TYLKO dla 2 klas i zakłada bimodalny histogram jasności (dwa „garby").
|
||||
Mnemonik: „PRÓG na bramce" — jak bramkarz, przepuszcza piksele jaśniejsze od T,
|
||||
blokuje ciemniejsze.
|
||||
|
||||
**Otsu** — automatyczny dobór progu. Algorytm: przetestuj WSZYSTKIE progi T=0..255, dla każdego oblicz wariancję wewnątrzklasową (jak „różnorodne" są piksele w klasie 0 i klasie 1). Wybierz T minimalizujące tę wariancję = klasy jak najbardziej jednorodne. Złożoność: O(n·L) gdzie n=piksele, L=poziomy jasności (256). Ograniczenie: działa TYLKO dla 2 klas i zakłada bimodalny histogram jasności (dwa „garby"). Patrz diagram powyżej.
|
||||
|
||||
Pseudokod Otsu:
|
||||
best_T = 0
|
||||
min_var = ∞
|
||||
for T in 0..255:
|
||||
c0 = piksele z jasność ≤ T
|
||||
c1 = piksele z jasność > T
|
||||
w0 = len(c0) / len(all_pixels)
|
||||
w1 = len(c1) / len(all_pixels)
|
||||
var = w0 * variance(c0) + w1 * variance(c1)
|
||||
if var < min_var:
|
||||
min_var = var
|
||||
best_T = T
|
||||
return best_T
|
||||
|
||||
Mnemonik: „AUTO-bramkarz Otsu" — sam sprawdza 256 progów i wybiera najlepszy.
|
||||
|
||||
---
|
||||
|
||||
#### Pojęcia kluczowe dla Region Growing
|
||||
|
||||
**Region Growing (rozrastanie regionu)** — zaczynasz od jednego piksela „ziarna" (seed) wybranego ręcznie lub automatycznie. Sprawdzasz sąsiadów: jeśli sąsiad jest PODOBNY (np. |jasność_sąsiada - jasność_regionu| < próg), dodaj go do regionu. Powtarzaj aż nie ma więcej podobnych sąsiadów. Następnie nowy seed → nowy region.
|
||||
|
||||
Seed piksel (100,100) ma jasność 150
|
||||
Sąsiad (101,100) ma jasność 153 → |153-150|=3 < próg 10 → DODAJ
|
||||
Sąsiad (100,101) ma jasność 200 → |200-150|=50 > próg 10 → ODRZUĆ (granica!)
|
||||
Region rośnie jak „plama" od seeda
|
||||
**Dlaczego seed „ręcznie LUB automatycznie"?** — to dwa różne scenariusze użycia:
|
||||
|
||||
Pseudokod:
|
||||
RĘCZNY seed:
|
||||
- Użytkownik klika myszką na obraz: „tu jest obiekt"
|
||||
- Użycie: segmentacja interaktywna (Photoshop „magic wand",
|
||||
narzędzia medyczne do zaznaczania guzów na RTG)
|
||||
- Zaleta: precyzyjny, użytkownik wie co chce segmentować
|
||||
- Wada: wymaga człowieka → nie skaluje się do 10 000 obrazów
|
||||
|
||||
AUTOMATYCZNY seed — metody:
|
||||
1. Siatka (grid): seed co N pikseli (np. co 50 px na obrazie 500×500 → 100 seedów)
|
||||
2. Lokalne ekstrema histogramu: znajdź najczęstszą jasność → seed tam
|
||||
3. Losowanie: wylosuj K punktów jako seedy
|
||||
4. Analiza gradientu: piksele w „płaskich" regionach (brak krawędzi) → dobre seedy
|
||||
|
||||
Dlaczego OR a nie AND?
|
||||
Bo to ALTERNATYWNE podejścia — albo człowiek wybiera (mało i precyzyjnie),
|
||||
albo algorytm wybiera (dużo i szybko, ale mniej precyzyjnie).
|
||||
|
||||

|
||||
|
||||
Pseudokod Region Growing:
|
||||
region = {seed}
|
||||
queue = [seed]
|
||||
while queue not empty:
|
||||
@ -90,106 +165,189 @@
|
||||
region.add(neighbor)
|
||||
queue.append(neighbor)
|
||||
|
||||
Problem: over-segmentation — drobne szumy → małe regiony
|
||||
|
||||
**Watershed (metoda zlewiska)** — traktuje obraz jak mapę topograficzną: wartość jasności piksela = wysokość terenu. Ciemne piksele = doliny, jasne = szczyty. Algorytm „zalewa" mapę wodą od najniższych punktów (minimów). Gdy woda z dwóch dolin się spotyka — tam jest GRANICA segmentu (grań).
|
||||
|
||||
Obraz jako mapa wysokości:
|
||||
████████
|
||||
██ ██ ← jasne piksele = szczyty (granice)
|
||||
█ dolina █
|
||||
█ (obiekt) █
|
||||
█ █
|
||||
██ dolina ██ ← kolejna dolina (inny segment)
|
||||
████████
|
||||
|
||||
Algorytm: zalewamy od dołu → woda spotyka się na graniach → SEGMENTY
|
||||
|
||||
Problem: MASYWNA over-segmentation — każde lokalne minimum (nawet szum) → osobna dolina
|
||||
Rozwiązanie: marker-controlled watershed — ręcznie podaj „ziarna" (markers)
|
||||
zamiast zalewać od KAŻDEGO minimum
|
||||
|
||||
**Mean Shift** — iteracyjne przesuwanie okna (jądra) do punktu o najwyższej gęstości pikseli w przestrzeni cech. Cechy to np. (jasność, x, y) lub (R, G, B, x, y). Piksele, które zbiegają do tego samego maksimum gęstości, tworzą jeden segment. Wolny: O(n²), ale nie wymaga podania liczby segmentów.
|
||||
|
||||
Wyobraź sobie rozsypane kulki na stole (= piksele w przestrzeni cech)
|
||||
Każda kulka „toczy się" w kierunku najbliższej „góry kulek" (max gęstości)
|
||||
Kulki, które dotoczyły się do tej samej góry → jeden segment
|
||||
|
||||
**Normalized Cuts** — modeluje obraz jako graf: piksele = węzły, krawędzie łączą sąsiednie piksele z wagą = PODOBIEŃSTWO (im bardziej podobne, tym wyższa waga). Szukamy CIĘCIA grafu (podział na grupy) minimalizującego stosunek ciętych krawędzi do rozmiaru grup. „Znormalizowane" → unika tworzenia malutkich segmentów. O(n³) — bardzo kosztowny: obraz 100×100 = 10 000 węzłów → 10¹² operacji!
|
||||
Mnemonik: „PLAMA atramentu" — seed to kropla atramentu na papierze,
|
||||
rozlewa się na podobne (jasne) miejsca, zatrzymuje się na granicach.
|
||||
|
||||
---
|
||||
|
||||
**Sieć neuronowa (neural network)** — model uczenia maszynowego inspirowany biologicznymi neuronami. Składa się z warstw „neuronów" — każdy neuron oblicza ważoną sumę wejść + bias, przepuszcza przez funkcję aktywacji (np. ReLU = max(0,x)), i przekazuje wynik dalej. Sieć uczy się automatycznie z danych: dostaje pary (obraz, poprawna mapa segmentacji), dostosowuje wagi by minimalizować błąd.
|
||||
#### Pojęcia kluczowe dla Watershed
|
||||
|
||||
Neuron: output = ReLU(w₁·x₁ + w₂·x₂ + ... + wₙ·xₙ + bias)
|
||||
ReLU(x) = max(0, x) — prosta, ale bardzo skuteczna funkcja aktywacji
|
||||
Uczenie: porównaj predykcję sieci z poprawną mapą (label) → oblicz błąd (loss)
|
||||
→ backpropagation → aktualizuj wagi → powtórz miliony razy
|
||||
**Watershed (metoda zlewiska)** — traktuje obraz jak mapę topograficzną: wartość jasności piksela = wysokość terenu. Ciemne piksele = doliny, jasne = szczyty. Algorytm „zalewa" mapę wodą od najniższych punktów (minimów). Gdy woda z dwóch dolin się spotyka — tam jest GRANICA segmentu (grań).
|
||||
|
||||
**CNN (Convolutional Neural Network)** — sieć, której kluczowym elementem jest warstwa konwolucyjna (splotowa). Zamiast łączyć KAŻDY piksel z KAŻDYM neuronem (co byłoby niewykonalne — obraz 640×480 = 307 200 neuronów wejściowych!), CNN przesuwany mały filtr (np. 3×3 pikseli) po obrazie, obliczając w każdym miejscu iloczyn skalarny filtra z fragmentem obrazu.
|
||||

|
||||
|
||||
Co robi konwolucja? Filtr 3×3 „jedzie" po obrazie jak wycieraczka:
|
||||
Algorytm:
|
||||
1. Zamień obraz na „mapę wysokości" (jasność = wysokość)
|
||||
2. Znajdź wszystkie lokalne minima (najciemniejsze punkty)
|
||||
3. „Zalewaj" od minimów — woda rośnie równomiernie
|
||||
4. Gdy woda z dwóch dolin się spotyka → postaw TAMĘ (granicę segmentu)
|
||||
5. Kontynuuj aż cały obraz zalany
|
||||
|
||||
Filtr (edge detector): Fragment obrazu: Wynik konwolucji:
|
||||
[-1 0 1] [50 50 200] (-1·50 + 0·50 + 1·200 +
|
||||
[-1 0 1] * [50 50 200] = -1·50 + 0·50 + 1·200 +
|
||||
[-1 0 1] [50 50 200] -1·50 + 0·50 + 1·200) = 450
|
||||
Problem: MASYWNA over-segmentation — każde lokalne minimum (nawet szum!) → osobna dolina
|
||||
Rozwiązanie: marker-controlled watershed — użytkownik podaje markery (seedy),
|
||||
zalewamy TYLKO od tych markerów
|
||||
|
||||
Duża wartość → tu jest KRAWĘDŹ (przejście ciemne→jasne)
|
||||
Mnemonik: „ZALEWANIE terenu" — wyobraź sobie model terenu z plasteliny w wannie.
|
||||
Powoli nalewasz wodę → doliny się wypełniają → granie gór = granice segmentów.
|
||||
|
||||
Hierarchia cech w CNN (wyuczona automatycznie!):
|
||||
Warstwa 1: krawędzie (|, —, /, \)
|
||||
Warstwa 2: tekstury (paski, siatki, plamy)
|
||||
Warstwa 3: części (koła, oczy, krawędź dachu)
|
||||
Warstwa 4+: obiekty (twarz, samochód, drzewo)
|
||||
---
|
||||
|
||||
**Encoder-Decoder** — architektura segmentacji: encoder ZMNIEJSZA rozdzielczość obrazu (downsampling — pooling), wydobywając coraz bardziej abstrakcyjne cechy (krawędzie → tekstury → obiekty). Decoder ZWIĘKSZA rozdzielczość (upsampling — dekonwolucja lub interpolacja), odtwarzając mapę segmentacji o pełnej rozdzielczości.
|
||||
#### Pojęcia kluczowe dla Mean Shift
|
||||
|
||||
Encoder (zmniejsza): [224×224] →pool→ [112×112] →pool→ [56×56] →pool→ [28×28] →pool→ [14×14]
|
||||
Decoder (zwiększa): [14×14] →up→ [28×28] →up→ [56×56] →up→ [112×112] →up→ [224×224]
|
||||
**Okno (window) / jądro (kernel)** — w kontekście Mean Shift to koło (lub kula w wielowymiarowej przestrzeni) o ustalonej szerokości (bandwidth = promień h) wokół aktualnego punktu. Wewnątrz okna algorytm oblicza „średnią ważoną" pozycji pikseli. Okno = jądro — to synonim. Nazwa „jądro" pochodzi od estymacji jądrowej gęstości (kernel density estimation, KDE).
|
||||
|
||||
Dlaczego nie sklasyfikować od razu KAŻDEGO piksela osobno?
|
||||
Bo pojedynczy piksel nie ma kontekstu — nie wiesz, czy piksel o wartości 150
|
||||
to fragment nieba czy samochodu. Encoder-decoder widzi KONTEKST (cały obiekt)
|
||||
i jednocześnie tworzy wynik o PEŁNEJ ROZDZIELCZOŚCI.
|
||||
Okno o promieniu h = 30 wokół punktu (100, 150):
|
||||
Bierze WSZYSTKIE piksele, których cechy (jasność, x, y)
|
||||
są w odległości ≤ 30 od (100, 150).
|
||||
Oblicza ich średnią → przesuwa okno NA TĘ ŚREDNIĄ.
|
||||
Powtarza aż okno się „zatrzyma" (przesunięcie < ε).
|
||||
|
||||
**Skip connections (połączenia skrótowe)** — połączenia „na skróty" łączące warstwy encodera z odpowiadającymi warstwami decodera. Problem: encoder traci detale przestrzenne (GDZIE dokładnie jest krawędź) podczas poolingu. Skip connections PRZENOSZĄ te detale z encodera wprost do decodera, umożliwiając precyzyjne granice segmentów.
|
||||
**Najwyższa gęstość (density peak)** — punkt w przestrzeni cech, gdzie jest NAJWIĘKSZE skupisko pikseli. Jak najwyższy szczyt góry w 3D. Mean Shift = „przesuń w kierunku średniej" → iteracyjnie zbliża się do szczytu gęstości.
|
||||
|
||||
Bez skip connections: decoder „wie" ŻE tu jest samochód, ale granice są rozmyte
|
||||
Ze skip connections: decoder „wie" ŻE tu jest samochód AND DOKŁADNIE GDZIE jest krawędź
|
||||
**Przestrzeń cech (feature space)** — każdy piksel jest opisany nie tylko pozycją (x, y) ale też cechami koloru (jasność, R, G, B). Przestrzeń cech to przestrzeń wielowymiarowa, np. (R, G, B, x, y) = 5 wymiarów. Piksele o podobnych kolorach i blisko siebie będą blisko w przestrzeni cech → tworzą klastry (skupiska).
|
||||
|
||||
**FCN (Fully Convolutional Network, 2015)** — pierwsza sieć w pełni konwolucyjna do segmentacji. Kluczowa innowacja: **zastąpienie warstw fully-connected** (FC → stały rozmiar wejścia) **konwolucjami** (→ dowolny rozmiar wejścia). Klasyczny CNN (np. VGG, AlexNet) kończy się warstwami FC, które wymagają stałego rozmiaru (np. 224×224). FCN zamienia FC na Conv 1×1, co pozwala przetwarzać obraz o DOWOLNYM rozmiarze i zwracać mapę segmentacji.
|
||||
Piksel A: (x=100, y=200, R=30, G=25, B=35) → punkt w 5D
|
||||
Piksel B: (x=102, y=201, R=32, G=27, B=33) → BLISKO A w 5D
|
||||
Piksel C: (x=105, y=198, R=200, G=210, B=220) → DALEKO od A w 5D (inny kolor!)
|
||||
→ A i B w jednym segmencie, C w innym
|
||||
|
||||
Klasyczny CNN: Conv → Conv → Pool → ... → FC(4096) → FC(1000) → "kot"
|
||||
FCN: Conv → Conv → Pool → ... → Conv1×1 → Upsample → mapa [H×W×C]
|
||||
↑ skip connections z encodera
|
||||
**Dlaczego Mean Shift NIE wymaga podania liczby segmentów?** — W K-means musisz podać K=3 (trzy klastry) ZANIM uruchomisz algorytm. Mean Shift działa inaczej: każdy piksel startuje i „toczy się" do najbliższego szczytu gęstości. Ile jest szczytów = tyle segmentów. Algorytm sam ODKRYWA liczbę klastrów. Parametrem jest tylko bandwidth (szerokość okna h): duże h → mało szczytów → mało segmentów; małe h → dużo szczytów → dużo segmentów.
|
||||
|
||||
**U-Net (2015)** — encoder-decoder w kształcie litery „U" ze skip connections realizowanymi przez **concatenation** (złączenie) — cechy z encodera są DOKLEJANE do cech decodera w odpowiedniej warstwie. Zaprojektowany dla segmentacji medycznej, gdzie zbiory danych są MAŁE (np. 30 zdjęć RTG), więc U-Net intensywnie używa data augmentation (obroty, odbicia, elastyczne deformacje).
|
||||

|
||||
|
||||
Encoder ──skip (concat)──→ Decoder
|
||||
↓ ──skip (concat)──→ ↑
|
||||
↓ ──skip (concat)──→ ↑
|
||||
bottleneck (najgłębsza warstwa)
|
||||
Pseudokod Mean Shift:
|
||||
for each pixel p:
|
||||
x = p.features # np. (R, G, B, pos_x, pos_y)
|
||||
repeat:
|
||||
window = all pixels within distance h from x
|
||||
x_new = weighted_mean(window)
|
||||
if |x_new - x| < epsilon:
|
||||
break
|
||||
x = x_new
|
||||
p.cluster = x # zbieżny punkt = ID klastra
|
||||
|
||||
Dlaczego „U"? Bo wizualnie encoder schodzi w dół (↓), bottleneck na dole,
|
||||
decoder wraca do góry (↑) — tworząc kształt litery U.
|
||||
Dlaczego concat a nie dodawanie? Więcej informacji — encoder features + decoder features
|
||||
→ sieć sama decyduje, które informacje wykorzystać.
|
||||
Mnemonik: „KULKI toczą się do dołków" — rozsyp kulki na nierównym stole,
|
||||
każda toczy się do najbliższego zagłębienia. Ile dołków = tyle segmentów.
|
||||
|
||||
**DeepLab v3+** — Google. Kluczowe innowacje:
|
||||
---
|
||||
|
||||
**Atrous (dilated) convolutions** — konwolucje z „dziurami" (fr. à trous = z dziurami). Standardowy filtr 3×3 patrzy na 3×3 = 9 sąsiednich pikseli. Atrous convolution z rate=2 patrzy na piksele z odstępem 2 — efektywnie widzi 5×5 obszar, ALE używa TYCH SAMYCH 9 parametrów (wag). Większe receptive field (pole widzenia) za darmo!
|
||||
#### Pojęcia kluczowe dla Normalized Cuts
|
||||
|
||||
Zwykła konwolucja 3×3: [x][x][x] receptive field = 3×3
|
||||
Dilated (rate=2): [x][ ][x][ ][x] receptive field = 5×5, 9 parametrów!
|
||||
Dilated (rate=3): [x][ ][ ][x][ ][ ][x] receptive field = 7×7, 9 parametrów!
|
||||
**Cięcie grafu (graph cut)** — graf to zbiór węzłów (pikseli) połączonych krawędziami (z wagami = podobieństwo). „Ciąć graf" to znaleźć LINIĘ dzielącą węzły na grupy, tak aby krawędzie „przecięte" tą linią miały niską wagę (= łączyły niepodobne piksele), a krawędzie wewnątrz grup miały wysoką wagę (= łączyły podobne piksele).
|
||||
|
||||
Dlaczego to ważne? Segmentacja wymaga KONTEKSTU — żeby wiedzieć, że piksel to
|
||||
„droga", musisz zobaczyć otaczające budynki i niebo. Większe receptive field = więcej kontekstu.
|
||||
**Jak szukamy cięcia?** — Naiwnie: sprawdź WSZYSTKIE możliwe podziały → wykładnicza złożoność. Normalized Cuts zamienia problem na rozwiązanie „problemu wartości własnych" (eigenvalue problem) macierzy Laplacianu grafu. Drugi najmniejszy wektor własny wskazuje, które piksele należą do grupy A (wartości dodatnie) a które do B (wartości ujemne).
|
||||
|
||||
**ASPP (Atrous Spatial Pyramid Pooling)** — równoległe zastosowanie atrous convolutions z WIELOMA rate (np. 6, 12, 18) + global average pooling, potem połączenie wyników. Każdy rate widzi kontekst w INNEJ skali → multi-scale features.
|
||||
**Dlaczego „znormalizowane" (normalized)?** — Zwykłe cięcie (min-cut) ma wadę: preferuje odcinanie MALUTKICH grup (1 piksel odcięty = małe cięcie). Normalizowanie dzieli koszt cięcia przez rozmiar grup → duże, zrównoważone segmenty.
|
||||
|
||||
**Transformer-based (SegFormer, Mask2Former)** — najnowsze podejście zastępujące CNN transformerami. Kluczowy mechanizm: **self-attention** — każdy piksel „pyta" WSZYSTKIE inne piksele: „jak bardzo jesteś ze mną powiązany?" CNN widzi tylko lokalne okno (3×3, 5×5), a self-attention widzi CAŁY obraz naraz → lepsze rozumienie globalnych zależności (np. „ten piksel jest częścią tego samego samochodu co piksel 500 pikseli dalej"). Cena: O(n²) pamięci (n = liczba pikseli), ale jakość SOTA na benchmarkach.
|
||||

|
||||
|
||||
Pseudokod Normalized Cuts (uproszczony):
|
||||
# 1. Zbuduj macierz podobieństwa W
|
||||
for each pair of pixels (i, j):
|
||||
W[i,j] = exp(-|color_i - color_j|^2 / sigma^2) # jeśli sąsiedzi
|
||||
W[i,j] = 0 # jeśli odlegli
|
||||
|
||||
# 2. Macierz stopni D
|
||||
D = diag(sum(W, axis=1)) # D[i,i] = suma wiersza i
|
||||
|
||||
# 3. Rozwiąż problem wartości własnych
|
||||
(D - W) * y = lambda * D * y
|
||||
# Weź DRUGI najm. wektor własny y (pierwszy = trywialny)
|
||||
|
||||
# 4. Podziel piksele
|
||||
segment_A = {i : y[i] > 0}
|
||||
segment_B = {i : y[i] <= 0}
|
||||
|
||||
Mnemonik: „CIĘCIE sznurków" — piksele połączone sznurkami (mocne = podobne).
|
||||
Tnij SŁABE sznurki → dwie grupy. Normalizacja = nie odcinaj samotnych pikseli.
|
||||
|
||||
---
|
||||
|
||||
#### Pojęcia kluczowe dla sieci neuronowych
|
||||
|
||||
**ReLU (Rectified Linear Unit)** — najpopularniejsza funkcja aktywacji w sieciach neuronowych. Wzór: ReLU(x) = max(0, x). Jeśli wejście jest ujemne → wynik = 0 (neuron „milczy"). Jeśli wejście jest dodatnie → wynik = x (neuron „przepuszcza" sygnał bez zmiany). Prosta, ale bardzo skuteczna — szybsza od starszych funkcji (sigmoid, tanh), bo nie wymaga obliczania exp().
|
||||
|
||||
ReLU(-3) = max(0, -3) = 0 ← neuron „wyłączony"
|
||||
ReLU(0) = max(0, 0) = 0 ← na granicy
|
||||
ReLU(2.5) = max(0, 2.5) = 2.5 ← neuron „włączony", przekazuje 2.5
|
||||
|
||||
Dlaczego nie po prostu f(x) = x (bez progu)?
|
||||
Bo liniowość → cała sieć = jedna warstwa liniowa (tracisz głębokość).
|
||||
ReLU jest NIELINIOWA (ma „zakręt" w 0) → pozwala sieci uczyć się
|
||||
skomplikowanych wzorców.
|
||||
|
||||

|
||||
|
||||
**Iloczyn skalarny (dot product)** — operacja na dwóch wektorach (listach liczb) dająca JEDNĄ liczbę. Mnożysz odpowiednie elementy parami i sumujesz wyniki. W CNN konwolucja = iloczyn skalarny filtra × fragment obrazu. Duży wynik = wektory „podobne" (filtr pasuje do fragmentu).
|
||||
|
||||
a = [1, 3, -2] b = [4, -1, 5]
|
||||
a · b = 1·4 + 3·(-1) + (-2)·5 = 4 - 3 - 10 = -9
|
||||
|
||||
W konwolucji:
|
||||
filtr = [-1, 0, 1, -1, 0, 1, -1, 0, 1] (spłaszczony 3×3)
|
||||
fragment = [50, 50, 200, 50, 50, 200, 50, 50, 200]
|
||||
dot = (-1)·50 + 0·50 + 1·200 + ... = 450 → duży = krawędź!
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
**Warstwa Fully Connected (FC, gęsta, dense)** — warstwa, w której KAŻDY neuron jest połączony z KAŻDYM wejściem. Obraz 7×7×512 (po konwolucjach) = 25 088 wartości. FC z 4096 neuronami = 25 088 × 4 096 = **~103 miliony wag**. Wady: (1) wymaga STAŁEGO rozmiaru wejścia (zawsze 7×7×512), (2) traci informację GDZIE coś jest (spłaszcza przestrzeń na wektor 1D).
|
||||
|
||||
**Konwolucja (convolution)** — operacja przesuwania małego filtra (np. 3×3) po obrazie. W każdej pozycji oblicza iloczyn skalarny filtra × fragment obrazu → jedną liczbę. TE SAME wagi filtra użyte w KAŻDEJ pozycji → dzielenie parametrów. Zachowuje informację przestrzenną (GDZIE coś jest).
|
||||
|
||||
**Conv 1×1 (konwolucja punktowa)** — filtr o rozmiarze 1×1 pikseli. „Patrzy" na JEDEN piksel, ale WSZYSTKIE kanały (np. 512). Działa jak FC, ale OSOBNO dla KAŻDEGO piksela → zachowuje mapę H×W. FCN zamienia FC na Conv 1×1: zamiast spłaszczyć 7×7×512 → 25 088 → FC, robi Conv1×1 na KAŻDYM z 7×7 pikseli × 512 kanałów → mapa 7×7×C (C = liczba klas).
|
||||
|
||||
**Jak FCN zamienia FC na Conv 1×1?** — Klasyczny CNN: ostatnia mapa cech 7×7×512 → FLATTEN → wektor 25 088 → FC → 1000 klas → „to jest kot". FCN: ostatnia mapa cech H×W×512 → Conv1×1(512→C) → mapa H×W×C → upsample do pełnej rozdzielczości. Kluczowa różnica: NIE spłaszczamy → możemy przetwarzać obraz o DOWOLNYM rozmiarze.
|
||||
|
||||
**Skip connections z encodera** — w encoder-decoder encoder zmniejsza obraz (pooling): 224→112→56→28→14. W tym procesie traci DETALE przestrzenne (dokładne krawędzie). Skip connections = „drogi na skróty" — cechy z wczesnych warstw encodera (pełne detali) są przekazywane WPROST do odpowiednich warstw decodera. Decoder wie CO i GDZIE.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
**U-Net — dlaczego kształt „U"?** — Narysuj architekturę: encoder zmniejsza rozdzielczość (bloki idą w DÓŁ po lewej stronie), bottleneck jest na dole, decoder zwiększa rozdzielczość (bloki idą W GÓRĘ po prawej stronie). Wizualnie tworzy literę „U". „Encoder schodzi w dół" = każda warstwa encodera ma MNIEJSZĄ rozdzielczość (224→112→56→28), wizualizowane jako bloki o malejącym rozmiarze ułożone jeden pod drugim.
|
||||
|
||||
**Concatenation (konkatenacja, złączenie)** — operacja „sklejania" dwóch tensorów wzdłuż osi kanałów. Jeśli encoder na poziomie 2 daje mapę 128×128×64 kanałów, a decoder na poziomie 2 daje mapę 128×128×64 kanałów, to concatenation = 128×128×**128** kanałów (64+64). Różni się od DODAWANIA (addition), które daje 128×128×64 (element-wise sum). Concatenation zachowuje WIĘCEJ informacji — sieć sama wybiera, które kanały wykorzystać.
|
||||
|
||||
Dodawanie (ResNet-style):
|
||||
encoder [a, b, c] + decoder [x, y, z] = [a+x, b+y, c+z] → 3 kanały
|
||||
|
||||
Concatenation (U-Net-style):
|
||||
encoder [a, b, c] ++ decoder [x, y, z] = [a, b, c, x, y, z] → 6 kanałów!
|
||||
→ więcej informacji, sieć sama zdecyduje co ważne
|
||||
|
||||

|
||||
|
||||
Mnemonik U-Net: „Litera U — w dół i w górę" — encoder schodzi ↓ (zmniejsza),
|
||||
decoder wraca ↑ (zwiększa), między nimi mosty (skip = concat).
|
||||
|
||||
---
|
||||
|
||||
**Receptive field (pole widzenia, pole recepcyjne)** — ile pikseli WEJŚCIOWYCH wpływa na JEDEN piksel wyjściowy. Konwolucja 3×3 → RF = 3×3. Dwie konwolucje 3×3 pod rząd → RF = 5×5 (druga widzi 3×3 fragmenty, z których każdy widział 3×3 → efektywnie 5×5). Większe RF = neuron widzi większy kontekst = lepiej rozumie co to za piksel.
|
||||
|
||||
**Dlaczego większe RF jest lepsze?** — Pojedynczy piksel o jasności 150 może być fragmentem nieba LUB samochodu. Patrząc na otoczenie 3×3 → nadal nie wiesz. Patrząc na otoczenie 50×50 → widzisz budynki obok → „to droga!". Segmentacja wymaga KONTEKSTU globalnego.
|
||||
|
||||
**Rate (współczynnik dylatacji)** — parametr atrous (dilated) convolution. Rate=1 = zwykła konwolucja (filtr dotyka sąsiadów). Rate=2 = filtr próbkuje co DRUGI piksel → RF rośnie z 3×3 do 5×5 przy TYCH SAMYCH 9 wagach. Rate=3 → RF = 7×7. Większy kontekst za darmo (bez dodatkowych parametrów).
|
||||
|
||||
**Global Average Pooling (GAP)** — operacja redukcji: mapa cech H×W×C → 1×1×C. Dla KAŻDEGO kanału oblicza ŚREDNIĄ ze wszystkich H×W pikseli. Wynik: jeden wektor o wymiarze C, reprezentujący „średnią informację" z całego obrazu. RF = nieskończone (cały obraz). Używane w ASPP DeepLab jako jedna z równoległych gałęzi.
|
||||
|
||||
Mapa cech 7×7×512:
|
||||
Kanał 0: macierz 7×7 wartości → średnia → jedna liczba
|
||||
Kanał 1: macierz 7×7 wartości → średnia → jedna liczba
|
||||
...
|
||||
Kanał 511: macierz 7×7 wartości → średnia → jedna liczba
|
||||
Wynik: wektor [avg₀, avg₁, ..., avg₅₁₁] → 1×1×512
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
**Transformer** — architektura sieci neuronowej zaproponowana w 2017 (Vaswani et al., „Attention Is All You Need"). Oryginalnie dla NLP (tłumaczenie), od 2020 (ViT — Vision Transformer) stosowana w wizji komputerowej. Kluczowy mechanizm: **self-attention** — każdy element (piksel/token) „pyta" WSZYSTKIE inne elementy: „jak bardzo jesteś ze mną powiązany?". Każdy element tworzy trzy wektory: Q (Query — czego szukam?), K (Key — co oferuję), V (Value — moja wartość). Attention = softmax(Q·Kᵀ / √d) · V. Koszt: O(n²) pamięci (n = liczba elementów).
|
||||
|
||||
**SOTA (State Of The Art)** — najlepszy znany wynik na danym benchmarku (zbiorze testowym) w danym momencie. Np. „Mask2Former osiąga mIoU 57.8% na ADE20K — to aktualny SOTA". SOTA ciągle się zmienia — każdy nowy paper może pobić poprzedni rekord.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
@ -202,8 +360,6 @@
|
||||
|
||||
**Focal Loss** — modyfikacja cross-entropy redukująca wpływ łatwych przykładów, skupiająca uczenie na trudnych. Kluczowa przy class imbalance (np. 99% tła, 1% obiekt).
|
||||
|
||||
**Receptive field** — ile wejścia „widzi" jeden neuron. Większe receptive field = kontekst globalny. Atrous convolutions zwiększają receptive field bez zwiększania parametrów.
|
||||
|
||||
---
|
||||
|
||||
### Problem: czym jest segmentacja obrazu?
|
||||
@ -242,28 +398,36 @@ Segmentacja obrazu to **przypisanie etykiety klasy KAŻDEMU pikselowi** obrazu.
|
||||
|
||||
Metody niewymagające uczenia maszynowego — oparte na ręcznie zdefiniowanych regułach (próg, podobieństwo, struktura grafu).
|
||||
|
||||
| Metoda | Idea | Wada | Złożoność |
|
||||
|--------|------|------|-----------|
|
||||
| **Thresholding** | piksel > T → klasa 1, else → klasa 0 | tylko 2 klasy, proste sceny | O(n) |
|
||||
| **Otsu** | automatyczny próg (min wariancja wewnątrzklasowa) | j.w. ale dobiera T sam | O(n·L) |
|
||||
| **Region Growing** | dodawaj sąsiednie piksele o podobnej wartości | over-segmentation, zależy od seeda | O(n) |
|
||||
| **Watershed** | obraz = mapa wysokości, granice = granie gór | over-segmentation | O(n log n) |
|
||||
| **Mean Shift** | iteracyjnie przesuwaj jądro do max gęstości | wolny | O(n²) |
|
||||
| **Normalized Cuts** | piksele = węzły grafu, minimalizuj znormalizowane cięcie | bardzo wolny | O(n³) |
|
||||
| Metoda | Idea | Wada | Złożoność | Mnemonik |
|
||||
|--------|------|------|-----------|----------|
|
||||
| **Thresholding** | piksel > T → klasa 1, else → klasa 0 | tylko 2 klasy, proste sceny | O(n) | „PRÓG na bramce" |
|
||||
| **Otsu** | automatyczny próg (min wariancja wewnątrzklasowa) | j.w. ale dobiera T sam | O(n·L) | „AUTO-bramkarz" |
|
||||
| **Region Growing** | dodawaj sąsiednie piksele o podobnej wartości | over-segmentation, zależy od seeda | O(n) | „PLAMA atramentu" |
|
||||
| **Watershed** | obraz = mapa wysokości, granice = granie gór | over-segmentation | O(n log n) | „ZALEWANIE terenu" |
|
||||
| **Mean Shift** | iteracyjnie przesuwaj jądro do max gęstości | wolny | O(n²) | „KULKI toczą się" |
|
||||
| **Normalized Cuts** | piksele = węzły grafu, minimalizuj znormalizowane cięcie | bardzo wolny | O(n³) | „CIĘCIE sznurków" |
|
||||
|
||||
**Przykład — Thresholding (Otsu):**
|
||||
#### DIY Przykład — Thresholding (Otsu) krok po kroku
|
||||
|
||||
Obraz grayscale: [30][200][180][45][210][190]
|
||||
Otsu automatycznie dobiera próg T=128:
|
||||
Wynik: [ 0 ][ 1 ][ 1 ][ 0][ 1 ][ 1 ]
|
||||
Zastosowanie: oddzielenie tekstu od tła (OCR), analiza zdjęć RTG
|
||||
Poniższy diagram pokazuje CAŁY pipeline progowania Otsu od obrazu wejściowego do wyniku. Obraz syntetyczny 64×64 z ciemnym kołem na jasnym tle — typowy przypadek bimodalny.
|
||||
|
||||
**Przykład — Watershed:**
|
||||

|
||||
|
||||
Obraz traktowany jako mapa topograficzna:
|
||||
Jasne piksele = szczyty, ciemne = doliny
|
||||
"Zalewamy" od minimów → woda spotyka się na graniach → GRANICE segmentów
|
||||
Problem: za wiele minimów → over-segmentation → potrzeba markers
|
||||
Pseudokod Otsu (Python-style):
|
||||
best_T, min_var = 0, float('inf')
|
||||
for T in range(256):
|
||||
c0 = pixels[pixels <= T] # piksele ciemne
|
||||
c1 = pixels[pixels > T] # piksele jasne
|
||||
if len(c0) == 0 or len(c1) == 0:
|
||||
continue
|
||||
w0 = len(c0) / len(pixels) # udział klasy 0
|
||||
w1 = len(c1) / len(pixels) # udział klasy 1
|
||||
var = w0 * variance(c0) + w1 * variance(c1) # σ² wewnątrzklasowa
|
||||
if var < min_var:
|
||||
min_var = var
|
||||
best_T = T
|
||||
# best_T = optymalny próg (np. 128)
|
||||
result = (pixels > best_T).astype(int) # binaryzacja
|
||||
|
||||
**Wspólna wada klasycznych metod:** wymagają ręcznego doboru parametrów (próg, seed, kernel), nie uczą się cech z danych, słabe na złożonych obrazach naturalnych.
|
||||
|
||||
@ -279,23 +443,26 @@ Metody uczące się automatycznie rozpoznawać cechy z danych treningowych. Wszy
|
||||
Decoder: cechy [14] → [28] → [56] → [112] → [224×224] (odtwarza MAPĘ)
|
||||
bottleneck
|
||||
|
||||
| Sieć | Rok | Kluczowa innowacja | Use case |
|
||||
|------|-----|-------------------|----------|
|
||||
| **FCN** | 2015 | w pełni konwolucyjna + skip connections | pierwsza end-to-end |
|
||||
| **U-Net** | 2015 | U-shape + skip concat + data augmentation | segmentacja medyczna |
|
||||
| **DeepLab v3+** | 2018 | atrous (dilated) conv + ASPP | general-purpose |
|
||||
| **SegFormer** | 2021 | transformer encoder (self-attention) | SOTA lightweight |
|
||||
| **Mask2Former** | 2022 | masked attention + unified architecture | SOTA universal |
|
||||
| Sieć | Rok | Kluczowa innowacja | Use case | Mnemonik |
|
||||
|------|-----|-------------------|----------|----------|
|
||||
| **FCN** | 2015 | w pełni konwolucyjna + skip connections | pierwsza end-to-end | „FC → Conv 1×1" |
|
||||
| **U-Net** | 2015 | U-shape + skip concat + data augmentation | segmentacja medyczna | „Litera U + mosty" |
|
||||
| **DeepLab v3+** | 2018 | atrous (dilated) conv + ASPP | general-purpose | „DZIURY w filtrze" |
|
||||
| **SegFormer** | 2021 | transformer encoder (self-attention) | SOTA lightweight | „WSZYSCY ze WSZYSTKIMI" |
|
||||
| **Mask2Former** | 2022 | masked attention + unified architecture | SOTA universal | „WSZYSCY ze WSZYSTKIMI" |
|
||||
|
||||
**FCN (Fully Convolutional Network):**
|
||||
|
||||
Mnemonik: „FC → Conv 1×1 = otwieramy bramkę dla DOWOLNEGO rozmiaru"
|
||||
Zwykły CNN: Conv → Conv → Pool → ... → FC → FC → "kot"
|
||||
FCN: Conv → Conv → Pool → ... → Conv → Upsample → mapa pikseli
|
||||
Innowacja: zamiana FC na Conv → wejście dowolnego rozmiaru
|
||||
FCN: Conv → Conv → Pool → ... → Conv1×1 → Upsample → mapa pikseli
|
||||
Innowacja: zamiana FC na Conv1×1 → wejście dowolnego rozmiaru
|
||||
Skip connections: łączą cechy z encodera → zachowują detale przestrzenne
|
||||
|
||||
**U-Net:**
|
||||
|
||||
Mnemonik: „Litera U + mosty" — schodzisz w dół, wracasz w górę,
|
||||
po drodze mosty (skip connections z concat) przenoszą detale.
|
||||
Encoder (↓) Decoder (↑)
|
||||
[64]────skip────→[64] ← skip connections = concatenation
|
||||
[128]───skip───→[128] (przenosi detale z encodera do decodera)
|
||||
@ -306,6 +473,8 @@ Metody uczące się automatycznie rozpoznawać cechy z danych treningowych. Wszy
|
||||
|
||||
**DeepLab v3+:**
|
||||
|
||||
Mnemonik: „DZIURY w filtrze" — filtr dosłownie ma dziury (à trous),
|
||||
przez co widzi dalej bez dodatkowych parametrów.
|
||||
Zwykła konwolucja 3×3: [x][x][x] receptive field = 3
|
||||
Dilated (rate=2): [x][ ][x][ ][x] receptive field = 5, te same parametry!
|
||||
ASPP: równolegle rate=6,12,18 → multi-scale features → łączenie
|
||||
@ -313,10 +482,34 @@ Metody uczące się automatycznie rozpoznawać cechy z danych treningowych. Wszy
|
||||
|
||||
**Transformery (SegFormer, Mask2Former):**
|
||||
|
||||
Mnemonik: „WSZYSCY ze WSZYSTKIMI" — każdy piksel rozmawia z KAŻDYM innym.
|
||||
CNN: filtr 3×3 widzi LOKALNY kontekst (sąsiadów)
|
||||
Transformer: self-attention widzi CAŁY obraz naraz
|
||||
Cena: O(n²) pamięci (n = piksele), ale lepsze wyniki
|
||||
|
||||
#### DIY Przykład — U-Net krok po kroku
|
||||
|
||||
Poniższy diagram pokazuje CAŁY pipeline U-Net od obrazu wejściowego do mapy segmentacji. Obraz syntetyczny 64×64 z dwoma obiektami (koła) na jasnym tle.
|
||||
|
||||

|
||||
|
||||
Pseudokod U-Net (PyTorch-style):
|
||||
# ENCODER — zmniejsza rozdzielczość, wyciąga cechy
|
||||
e1 = conv_block(input, filters=64) # [64×64×64]
|
||||
e2 = conv_block(maxpool(e1), filters=128) # [32×32×128]
|
||||
e3 = conv_block(maxpool(e2), filters=256) # [16×16×256]
|
||||
|
||||
# BOTTLENECK — najgłębsza warstwa
|
||||
b = conv_block(maxpool(e3), filters=512) # [8×8×512]
|
||||
|
||||
# DECODER — zwiększa rozdzielczość + skip connections (concat!)
|
||||
d3 = conv_block(concat(upconv(b), e3), filters=256) # [16×16×256]
|
||||
d2 = conv_block(concat(upconv(d3), e2), filters=128) # [32×32×128]
|
||||
d1 = conv_block(concat(upconv(d2), e1), filters=64) # [64×64×64]
|
||||
|
||||
# WYNIK — Conv 1×1 → mapa klas
|
||||
output = conv_1x1(d1, n_classes=3) # [64×64×3] → argmax → [64×64] etykiety
|
||||
|
||||
---
|
||||
|
||||
### Metryki i funkcje kosztu
|
||||
@ -334,7 +527,38 @@ Metody uczące się automatycznie rozpoznawać cechy z danych treningowych. Wszy
|
||||
|
||||
### Jak zapamiętać
|
||||
|
||||
- **U-Net = „U-shape + skip connections"** — encoder-decoder
|
||||
- **DeepLab = „Atrous (dilated) convolutions + ASPP"**
|
||||
- **mIoU = Intersection / Union, uśrednione per klasa**
|
||||
**Super-mnemonik na kolejność algorytmów:**
|
||||
|
||||
„Turyści Oglądają Rzekę, Wodospad, Morze, Nurt — Fotografują Uroczy Dwór Tajemnic"
|
||||
|
||||
Klasyczne: Thresholding → Otsu → Region growing → Watershed → Mean shift → Normalized cuts
|
||||
Neuronowe: FCN → U-Net → DeepLab → Transformer
|
||||
|
||||

|
||||
|
||||
**Mnemoniki per algorytm — STRATEGIE KLASYCZNE:**
|
||||
|
||||
| Algorytm | Mnemonik | Skojarzenie |
|
||||
|----------|----------|-------------|
|
||||
| **Thresholding** | „PRÓG na bramce" | Bramkarz przepuszcza piksele > T, blokuje ≤ T |
|
||||
| **Otsu** | „AUTO-bramkarz" | Sam sprawdza 256 progów, wybiera najlepszy (min σ²) |
|
||||
| **Region Growing** | „PLAMA atramentu" | Kropla atramentu rozlewa się na podobne piksele (BFS) |
|
||||
| **Watershed** | „ZALEWANIE terenu" | Woda zalewa doliny, granie gór = granice segmentów |
|
||||
| **Mean Shift** | „KULKI toczą się do dołków" | Każda kulka → max gęstości, ile dołków = tyle segmentów |
|
||||
| **Normalized Cuts** | „CIĘCIE sznurków" | Tnij słabe sznurki (krawędzie grafu), zachowaj silne |
|
||||
|
||||
**Mnemoniki per algorytm — SIECI NEURONOWE:**
|
||||
|
||||
| Sieć | Mnemonik | Skojarzenie |
|
||||
|------|----------|-------------|
|
||||
| **FCN** | „FC → Conv 1×1" | Otwiera bramkę dla dowolnego rozmiaru wejścia |
|
||||
| **U-Net** | „Litera U + mosty" | Schodzisz ↓, wracasz ↑, mosty (skip concat) przenoszą detale |
|
||||
| **DeepLab** | „DZIURY w filtrze" | Filtr ma dziury (à trous) → widzi dalej bez dodatkowych wag |
|
||||
| **Transformer** | „WSZYSCY ze WSZYSTKIMI" | Każdy piksel pyta każdy inny (self-attention, O(n²)) |
|
||||
|
||||
**Mnemoniki per metrykę:**
|
||||
|
||||
- **mIoU** = „Nakładka / Suma" → intersection / union, uśrednione per klasa
|
||||
- **Dice** = „Dwie nakładki / Razem" → 2·|A∩B| / (|A|+|B|)
|
||||
- **Focal** = „Fokus na TRUDNYCH" → trudne piksele ważą więcej
|
||||
|
||||
|
||||
@ -135,17 +135,7 @@
|
||||
|
||||
**Architektura CNN — pełny przykład (AlexNet, wygrał ImageNet 2012):**
|
||||
|
||||
Obraz [224×224×3] ← 150 528 wartości (piksele RGB)
|
||||
↓ Conv1: 96 filtrów 11×11, stride 4
|
||||
[55×55×96] ← 96 map cech, każda 55×55
|
||||
↓ MaxPool 3×3, stride 2
|
||||
[27×27×96]
|
||||
↓ Conv2: 256 filtrów 5×5
|
||||
[27×27×256]
|
||||
↓ MaxPool → Conv3-5 → MaxPool
|
||||
[6×6×256] = 9 216 liczb spłaszczonych
|
||||
↓ FC(4096) → FC(4096) → FC(1000) → Softmax
|
||||
→ "golden retriever" (klasa 207, pewność 0.89)
|
||||

|
||||
|
||||
ROZMIARY MALEJĄ: 224 → 55 → 27 → 13 → 6 (kompresja przestrzenna)
|
||||
KANAŁY ROSNĄ: 3 → 96 → 256 → 384 → 256 (coraz więcej wyuczonych cech)
|
||||
@ -211,14 +201,7 @@
|
||||
|
||||
**FPN (Feature Pyramid Network)** — technika łączenia feature map z RÓŻNYCH warstw backbone'u. Wczesne warstwy (wysoka rozdzielczość) → małe obiekty. Późne warstwy (niska rozdzielczość) → duże obiekty. FPN łączy obie → wykrywa obiekty WSZYSTKICH rozmiarów.
|
||||
|
||||
Backbone (ResNet):
|
||||
Warstwa 1: 56×56 → dużo detali, dobre dla MAŁYCH obiektów
|
||||
Warstwa 2: 28×28 → średnie obiekty
|
||||
Warstwa 3: 14×14 → duże obiekty
|
||||
Warstwa 4: 7×7 → bardzo duże obiekty
|
||||
|
||||
FPN: łączy top-down (7×7 → 14×14 → 28×28 → 56×56) + lateral connections
|
||||
→ predykcje na KAŻDYM poziomie → małe I duże obiekty!
|
||||

|
||||
|
||||
---
|
||||
|
||||
@ -301,15 +284,7 @@
|
||||
|
||||
**Support Vectors** — punkty danych NAJBLIŻSZE hiperpłaszczyźnie. To one „podpierają" (support) margines i definiują pozycję hiperpłaszczyzny. Reszta punktów jest nieistotna! Nazwa: „wektory nośne" — bo to wektory cech, które „niosą" decyzję.
|
||||
|
||||
Przestrzeń 2D: O = klasa "pie szy" X = klasa "nie-pieszy"
|
||||
O O
|
||||
O O
|
||||
hiperpłaszczyzna → ─ ─ ─ ─ ─ ─ ─ ─ ← margines ↕
|
||||
X X
|
||||
X X X
|
||||
|
||||
Support vectors: O i X najbliższe linii (zaznaczone pogrubione)
|
||||
SVM: przesuń linię tak, żeby margines ↕ był MAKSYMALNY
|
||||

|
||||
|
||||
**HOG+SVM — klasyczny pipeline detekcji pieszych:**
|
||||
|
||||
@ -326,15 +301,7 @@
|
||||
|
||||
**Haar features (cechy Haarowe)** — najprostsze cechy obrazowe: prostokąty podzielone na jasną i ciemną część. Wartość cechy = (suma pikseli jasnych) − (suma pikseli ciemnych). Proste, ale skuteczne — wykrywają kontrasty typowe dla twarzy.
|
||||
|
||||
Przykłady cech Haar:
|
||||
Krawędź pionowa: Krawędź pozioma: Linia (3 prostokąty):
|
||||
┌──────┬──────┐ ┌────────────┐ ┌────┬──────┬────┐
|
||||
│JASNY │CIEMNY│ │ JASNY │ │CIEM│JASNY │CIEM│
|
||||
│ +Σ₁ │ -Σ₂ │ │ +Σ₁ │ │ -Σ₁│ +Σ₂ │ -Σ₃│
|
||||
│ │ │ ├────────────┤ │ │ │ │
|
||||
└──────┴──────┘ │ CIEMNY │ └────┴──────┴────┘
|
||||
wartość = Σ₁ − Σ₂ │ -Σ₂ │ wartość = Σ₂ − Σ₁ − Σ₃
|
||||
└────────────┘
|
||||

|
||||
|
||||
Dlaczego działa na TWARZACH?
|
||||
- Oczy CIEMNIEJSZE niż czoło → cecha "krawędź pozioma" daje dużą wartość
|
||||
@ -348,14 +315,7 @@
|
||||
|
||||
Jak? Integral Image[x,y] = suma WSZYSTKICH pikseli od (0,0) do (x,y).
|
||||
|
||||
Obraz oryginalny: Integral Image (sumy kumulatywne):
|
||||
[1 2 3] [ 1 3 6]
|
||||
[4 5 6] [ 5 12 21]
|
||||
[7 8 9] [12 27 45]
|
||||
|
||||
Chcemy sumę prostokąta (1,1)-(2,2) = piksele [5,6,8,9] = 28:
|
||||
Z Integral Image: II[2,2] − II[0,2] − II[2,0] + II[0,0]
|
||||
= 45 − 6 − 12 + 1 = 28 ✓
|
||||

|
||||
|
||||
Zawsze 4 odczyty z tabeli → O(1)!
|
||||
Czy prostokąt ma 4 piksele czy 4 MILIONY — czas TEN SAM!
|
||||
@ -389,17 +349,7 @@
|
||||
|
||||
**Cascade (kaskada klasyfikatorów)** — genialna optymalizacja szybkości: zamiast sprawdzać WSZYSTKIE 200 cech na każdym oknie, użyj KASKADY etapów. Każdy etap = prosty klasyfikator, który szybko ODRZUCA "na pewno nie-twarz".
|
||||
|
||||
Etap 1: 2 cechy → odrzuca 50% okien (czas: ~1 μs)
|
||||
Etap 2: 10 cech → odrzuca 80% reszty (czas: ~5 μs)
|
||||
Etap 3: 25 cech → odrzuca 90% reszty
|
||||
...
|
||||
Etap 25: 200 cech → szczegółowa analiza (czas: ~100 μs)
|
||||
|
||||
Sliding window: ~500 000 okien do sprawdzenia (różne pozycje × skale)
|
||||
BEZ kaskady: 500 000 × 200 cech = WOLNO
|
||||
Z kaskadą: 99% okien odrzuconych w etapach 1-3 (za ~5μs każde!)
|
||||
Tylko 0.01% dochodzi do etapu 25
|
||||
→ CAŁY obraz w ~30ms = 30+ fps = REAL-TIME!
|
||||

|
||||
|
||||
Mnemonik: kaskada = "SITO" — coraz drobniejsze oczka,
|
||||
na początku odpada piach, na końcu zostaje ZŁOTO (twarz).
|
||||
@ -414,7 +364,7 @@
|
||||
|
||||
---
|
||||
|
||||

|
||||

|
||||
|
||||
**R-CNN family (two-stage detectors)** — dwuetapowe: najpierw generuj propozycje regionów, potem klasyfikuj każdy region. Nazwa: Region-based CNN.
|
||||
|
||||
@ -467,13 +417,7 @@
|
||||
3. W każdej komórce weź MAX (jak max pooling)
|
||||
4. Wynik: tensor 7×7 — STAŁY rozmiar niezależnie od oryginalnego ROI!
|
||||
|
||||
Przykład (ROI Pool 2×2 dla prostoty):
|
||||
ROI na feature mapie [4×4]: Po ROI Pool 2×2:
|
||||
[1 3 | 2 1] [5 6] ← max(1,3,0,5)=5 max(2,1,1,6)=6
|
||||
[0 5 | 1 6] [7 9] ← max(0,4,7,2)=7 max(1,0,9,1)=9
|
||||
─────────────
|
||||
[0 4 | 1 0]
|
||||
[7 2 | 9 1]
|
||||

|
||||
|
||||
Kluczowa sztuczka Fast R-CNN:
|
||||
CNN raz na CAŁY obraz → JEDNA feature mapa → ROI Pool 2000 regionów z TEJ SAMEJ mapy
|
||||
@ -544,22 +488,14 @@
|
||||
- C prawdopodobieństw klas = „jaki to obiekt?"
|
||||
Jedno przejście przez sieć → WSZYSTKIE detekcje naraz. 45-155 fps!
|
||||
|
||||
Jak to działa wizualnie (S=7, B=2, C=20 klas jak w Pascal VOC):
|
||||
|
||||
Obraz [448×448] → CNN (24 warstwy konwolucyjne + 2 FC) → tensor 7×7×30
|
||||
↑
|
||||
30 = 2×(4+1) + 20
|
||||
2 bbox × (x,y,w,h,conf) + 20 klas
|
||||
|
||||
Komórka (3,4) predykuje: bbox1=(0.3, 0.7, 0.4, 0.6, 0.92), klasa="samochód" (p=0.88)
|
||||
→ „środek samochodu jest w komórce (3,4), bbox ma takie wymiary, pewność 92%"
|
||||
|
||||
Potem NMS: usuwa duplikaty (wiele komórek może wykryć ten sam obiekt)
|
||||

|
||||
|
||||
**SSD (Single Shot MultiBox Detector, 2016)** — ulepsza YOLO przez multi-scale feature maps: predykcje z WIELU warstw CNN, każda o innej rozdzielczości. Wczesne warstwy (wysoka rozdzielczość) wykrywają MAŁE obiekty; późne warstwy (niska rozdzielczość) wykrywają DUŻE. Anchor boxes predefiniowane na każdej skali.
|
||||
|
||||
**Anchor box (kotwica)** — predefiniowany prostokąt o określonym kształcie/proporcji (np. 1:1, 1:2, 2:1). Sieć NIE predykuje bbox od zera — predykuje PRZESUNIĘCIE (offset) od najbliższego anchora. Łatwiejsze zadanie! Wiele anchorów → pokrycie różnych kształtów obiektów (osoby = wysoki prostokąt, samochód = szeroki).
|
||||
|
||||

|
||||
|
||||
**Anchor-free** — nowoczesne podejście (FCOS, YOLOv8): bezpośrednia predykcja środka i wymiarów, bez predefiniowanych anchorów. Prostsza architektura, mniej hyperparametrów.
|
||||
|
||||
**Transformer** — architektura sieci neuronowej pierwotnie z NLP (2017, "Attention is All You Need"), ale skutecznie zaadaptowana do wizji komputerowej (ViT, DETR). Kluczowy mechanizm: **self-attention** — każdy element wejścia "patrzy" na WSZYSTKIE inne elementy i decyduje, które są dla niego ważne.
|
||||
@ -587,10 +523,7 @@ Jedno przejście przez sieć → WSZYSTKIE detekcje naraz. 45-155 fps!
|
||||
|
||||
**DETR (DEtection TRansformer, 2020)** — model Facebooka stosujący Transformer do detekcji. Radykalnie prostszy pipeline: BRAK anchorów, BRAK NMS! Sieć predykuje bezpośrednio ZESTAW N obiektów (np. N=100).
|
||||
|
||||
Pipeline DETR:
|
||||
Obraz → CNN backbone → Feature mapa → Transformer Encoder (self-attention)
|
||||
→ Transformer Decoder (z N=100 "object queries")
|
||||
→ N predykcji: [(klasa₁, bbox₁), ..., (klasa₁₀₀, bbox₁₀₀)]
|
||||

|
||||
|
||||
"Object queries" = 100 wyuczonych wektorów, każdy "szuka" jednego obiektu.
|
||||
Obraz z 5 obiektami → 5 queries dopasuje się do obiektów,
|
||||
@ -655,13 +588,7 @@ Jedno przejście przez sieć → WSZYSTKIE detekcje naraz. 45-155 fps!
|
||||
|
||||
**IoU (Intersection over Union)** — miara nakładania dwóch prostokątów. IoU = pole przecięcia / pole sumy. Wartości: 0.0 (nie nakładają się) do 1.0 (identyczne).
|
||||
|
||||
bbox A: bbox B: Przecięcie:
|
||||
┌──────────┐ ┌────┐
|
||||
│ A │ ┌──────────┐ │ ∩ │
|
||||
│ ┌───┼────────┼──┐ │ └────┘
|
||||
│ │ ∩ │ │ │ B │
|
||||
└──────┼───┘ │ │ │
|
||||
└────────────┴──┘
|
||||

|
||||
|
||||
IoU = pole(∩) / pole(A ∪ B)
|
||||
= pole(∩) / (pole(A) + pole(B) − pole(∩))
|
||||
@ -696,11 +623,7 @@ Detekcja obiektów to **lokalizacja** (gdzie?) i **klasyfikacja** (co?) obiektó
|
||||
|
||||
**Porównanie z innymi zadaniami:**
|
||||
|
||||
Zadanie Wynik Przykład
|
||||
─────────────────────────────────────────────────────────
|
||||
Klasyfikacja "kot" (1 etykieta) cały obraz → 1 klasa
|
||||
Detekcja bbox + klasa (N obiektów) prostokąty wokół obiektów
|
||||
Segmentacja etykieta per piksel maska pikseli
|
||||

|
||||
|
||||
---
|
||||
|
||||
@ -713,21 +636,134 @@ Metody sprzed deep learningu — ręcznie projektowane cechy (features) + klasyc
|
||||
| **HOG + SVM** | 2005 | Histogram of Oriented Gradients | SVM | wolna (~1 fps) | detekcja pieszych |
|
||||
| **Viola-Jones** | 2001 | Haar features + Integral Image | AdaBoost cascade | real-time (30+ fps) | detekcja twarzy |
|
||||
|
||||
**HOG + SVM (Dalal & Triggs, 2005):**
|
||||
#### HOG + SVM (Dalal & Triggs, 2005) — krok po kroku
|
||||
|
||||
Pipeline: Obraz → Sliding window → HOG (histogramy gradientów) → SVM → detekcja/brak
|
||||
HOG: dzieli okno na komórki (8×8 px), liczy histogramy kierunków krawędzi
|
||||
SVM: "czy ten wzorzec krawędzi to człowiek?"
|
||||
Wada: ręczne cechy, wolny sliding window, działa dobrze TYLKO na pieszych
|
||||
**Mnemonik kroków HOG: „GÓRA KOCHA BOGATYCH NARCIARZY" → Gradienty → Orientacja → Komórki → Bloki → Normalizacja**
|
||||
|
||||
**Viola-Jones (2001) — 3 innowacje:**
|
||||

|
||||
|
||||
1. Haar features: [ jasne | ciemne ] → prosta różnica intensywności
|
||||
2. Integral Image: suma prostokąta w O(1), niezależnie od rozmiaru!
|
||||
3. Cascade: Etap 1 (2 cechy): odrzuca 50% okien w 1 μs
|
||||
Etap 2 (10 cech): odrzuca 80% reszty
|
||||
...Etap 25 (200 cech): szczegółowa analiza TYLKO 0.01% okien
|
||||
Efekt: ~95% detections = szybkie odrzucenia → real-time!
|
||||
**Krok 1 — Gradienty (G jak GÓRA):** Oblicz gradient KAŻDEGO piksela. Gradient = „siła i kierunek zmiany jasności". Tam, gdzie jasność skacze (np. 50→200), jest krawędź.
|
||||
|
||||
Przykład liczbowy:
|
||||
Piksele w wierszu: [50, 50, 200]
|
||||
Gx = pixel[x+1] − pixel[x−1] = 200 − 50 = 150 ← silna krawędź pionowa!
|
||||
Gy = analogicznie w pionie
|
||||
Siła: magnitude = √(Gx² + Gy²) = √(150² + 0²) = 150
|
||||
Kierunek: direction = arctan(Gy/Gx) = arctan(0/150) = 0° (krawędź pionowa)
|
||||
|
||||
**Krok 2 — Orientacja (O jak KOCHA):** Każdy piksel głosuje na kierunek swojej krawędzi. 9 „koszyków" (binów) co 20°: 0°, 20°, 40°, …, 160°. Głos ważony SIŁĄ gradientu (silniejsza krawędź = mocniejszy głos).
|
||||
|
||||
Piksel z magnitude=150, direction=10°:
|
||||
Głosuje na bin 0° (z wagą proporcjonalną do bliskości) i bin 20°
|
||||
Piksel z magnitude=30, direction=85°:
|
||||
Głosuje na bin 80° i bin 100° (słabsza krawędź = słabszy głos)
|
||||
|
||||
**Krok 3 — Komórki (K jak BOGATYCH):** Podziel okno (64×128 px) na komórki 8×8 pikseli = 8×16 = 128 komórek. Dla KAŻDEJ komórki stwórz histogram 9 binów — to jej „odcisk palca kierunkowości krawędzi".
|
||||
|
||||

|
||||
|
||||
**Krok 4 — Bloki (B jak NARCIARZY):** Grupuj komórki w bloki 2×2 (= 16×16 px). Przesuwaj blok z krokiem 1 komórki. Okno 64×128 → (8−1)×(16−1) = 7×15 = 105 bloków.
|
||||
|
||||
**Krok 5 — Normalizacja (N):** Dla KAŻDEGO bloku (4 komórki × 9 binów = 36 wartości) wykonaj normalizację L2 → odporność na zmiany oświetlenia. 105 bloków × 36 = **3780 cech** → wektor HOG.
|
||||
|
||||
Pseudokod:
|
||||
def compute_hog(window_64x128):
|
||||
Gx = pixel[x+1] - pixel[x-1] # gradient poziomy
|
||||
Gy = pixel[y+1] - pixel[y-1] # gradient pionowy
|
||||
mag = sqrt(Gx**2 + Gy**2) # siła
|
||||
dir = arctan2(Gy, Gx) * 180 / pi # kierunek 0°-180°
|
||||
|
||||
hog = []
|
||||
for block_2x2 in sliding_blocks(cells_8x8):
|
||||
block_hist = []
|
||||
for cell in block_2x2: # 4 komórki
|
||||
hist = [0]*9 # 9 binów
|
||||
for px in cell.pixels: # 64 piksele
|
||||
bin = int(dir[px] / 20) # który bin?
|
||||
hist[bin] += mag[px] # ważone głosowanie
|
||||
block_hist += hist
|
||||
block_hist = L2_normalize(block_hist) # normalizacja!
|
||||
hog += block_hist
|
||||
return hog # wektor 3780 cech → do SVM
|
||||
|
||||
**Krok 6 — SVM klasyfikuje:** Wektor 3780 cech → SVM odpowiada: „pieszy" (+1) lub „tło" (−1).
|
||||
|
||||

|
||||
|
||||
Mnemonik SVM: „LINIA MAKSYMALNEGO ODDECHU"
|
||||
SVM = linia (hiperpłaszczyzna) z MAKSYMALNYM marginesem.
|
||||
Jak MOST nad rzeką — im szerszy, tym bezpieczniejszy (lepiej generalizuje).
|
||||
|
||||
**Krok 7 — NMS:** Usuń duplikaty (wiele okien wykryło tego samego pieszego → zachowaj najlepsze).
|
||||
|
||||
Mnemonik PEŁNEGO pipeline'u HOG+SVM: „GOKBN-SN"
|
||||
→ Gradienty → Orientacja → Komórki → Bloki → Normalizacja → SVM → NMS
|
||||
= „Grasz Ostro, Kumplu? Bądź Naturalny, Szybko Nabierz (wprawy)!"
|
||||
|
||||
---
|
||||
|
||||
#### Viola-Jones (2001) — krok po kroku
|
||||
|
||||
**Mnemonik 3 innowacji: „HIC" → Haar + Integral Image + Cascade**
|
||||
|
||||
**Innowacja 1 — Haar features (H):** Prostokąty dzielone na jasną i ciemną część. Wartość = Σ(jasna) − Σ(ciemna). Proste, ale wykrywają kontrasty typowe dla twarzy.
|
||||
|
||||

|
||||
|
||||
Pseudokod cechy Haar:
|
||||
def haar_edge_vertical(img, x, y, w, h):
|
||||
left_sum = sum_pixels(img, x, y, x+w//2, y+h) # jasna połówka
|
||||
right_sum = sum_pixels(img, x+w//2, y, x+w, y+h) # ciemna połówka
|
||||
return left_sum - right_sum # duża wartość = silna krawędź
|
||||
|
||||
Mnemonik: Haar = „Hej, A tu jest Różnica?"
|
||||
Cechy Haar pytają: „Czy lewa strona JAŚNIEJSZA niż prawa?"
|
||||
|
||||
**Innowacja 2 — Integral Image (I):** Precomputed tabela: suma DOWOLNEGO prostokąta w O(1) — 4 odczyty z tabeli, niezależnie od rozmiaru!
|
||||
|
||||

|
||||
|
||||
Pseudokod:
|
||||
def build_integral_image(img):
|
||||
II = zeros(H, W)
|
||||
for y in range(H):
|
||||
for x in range(W):
|
||||
II[y][x] = img[y][x] + II[y-1][x] + II[y][x-1] - II[y-1][x-1]
|
||||
return II
|
||||
|
||||
def rect_sum(II, x1, y1, x2, y2): # ZAWSZE O(1)!
|
||||
return II[y2][x2] - II[y1-1][x2] - II[y2][x1-1] + II[y1-1][x1-1]
|
||||
|
||||
Mnemonik: Integral Image = „4 Odczyty I Gotowe!" = 4OIG
|
||||
Jak czytanie z gotowej tabeli: nie liczymy, tylko odczytujemy!
|
||||
|
||||
**Innowacja 3 — Cascade (C):** Kaskada etapów — szybkie odrzucanie „na pewno nie-twarz".
|
||||
|
||||

|
||||
|
||||
Pseudokod:
|
||||
def cascade_classify(window):
|
||||
for stage in [stage_1, stage_2, ..., stage_25]:
|
||||
score = sum(stage.weights[i] * haar_feature[i](window)
|
||||
for i in stage.features)
|
||||
if score < stage.threshold:
|
||||
return "NIE-TWARZ" # szybkie odrzucenie!
|
||||
return "TWARZ" # przeszło WSZYSTKIE etapy
|
||||
|
||||
Mnemonik: Cascade = „SITO z coraz drobniejszymi oczkami"
|
||||
Etap 1: sito o dużych oczkach → odpada piach (oczywiste nie-twarze)
|
||||
Etap 25: sito najdrobniejsze → zostaje ZŁOTO (twarz)
|
||||
99% okien odpada w pierwszych 3 etapach → REAL-TIME!
|
||||
|
||||
**Pełny pipeline Viola-Jones:**
|
||||
|
||||
1. Sliding window (24×24) po obrazie w wielu skalach
|
||||
2. Integral Image (preprocessing, O(n) — raz)
|
||||
3. Dla każdego okna: kaskada (Haar + AdaBoost, najczęściej odrzuci w 1-3 etapie)
|
||||
4. NMS na detekcjach → wynik
|
||||
|
||||
Mnemonik pipeline'u: „SIKN" = Sliding → Integral → Kaskada → NMS
|
||||
= „Szybko Identyfikuj Kształty Niezwykłe!"
|
||||
|
||||
---
|
||||
|
||||
@ -746,6 +782,8 @@ Metody sprzed deep learningu — ręcznie projektowane cechy (features) + klasyc
|
||||
Fast R-CNN: [CNN raz] → [ROI Pool 2000 regionów] → [FC] = 2s lepiej
|
||||
Faster R-CNN:[CNN] → [RPN generuje propozycje] → [ROI Pool] → [FC] = 0.2s!
|
||||
|
||||

|
||||
|
||||
**One-stage detectors (jednoetapowe)** — klasyfikacja i lokalizacja w JEDNYM przejściu.
|
||||
|
||||
| Model | Rok | Szybkość | Innowacja |
|
||||
@ -763,13 +801,7 @@ Metody sprzed deep learningu — ręcznie projektowane cechy (features) + klasyc
|
||||
|
||||
**Two-stage vs One-stage:**
|
||||
|
||||
Cecha Two-stage (Faster R-CNN) One-stage (YOLO)
|
||||
─────────────────────────────────────────────────────────────────
|
||||
Szybkość ~5 fps 45-155 fps
|
||||
Dokładność (mAP) wyższa (historycznie) dorównuje (YOLOv8)
|
||||
Małe obiekty lepszy gorszy (ale SSD/FPN pomaga)
|
||||
Architektura 2 etapy + NMS 1 etap + NMS (DETR: bez NMS)
|
||||
Real-time? nie TAK
|
||||

|
||||
|
||||
---
|
||||
|
||||
@ -777,49 +809,205 @@ Metody sprzed deep learningu — ręcznie projektowane cechy (features) + klasyc
|
||||
|
||||
Masz wytrenowany klasyfikator (np. ResNet na ImageNet: obraz → „kot"). Jak go użyć do **lokalizacji** obiektów?
|
||||
|
||||
**Podejście 1 — Sliding Window (najwolniejsze):**
|
||||
**Mnemonik 3 podejść: „SRF" = „Sliding → Region → Fine-tune" = „Szukaj Ręcznie, Finalnie optymalizuj!"**
|
||||
|
||||
Wytnij okno → klasyfikuj → przesuń → powtórz → NMS
|
||||
Obraz 640×480, okno 64×64, krok 8px, 5 skal:
|
||||
~240 000 pozycji × 5 skal = ~1 200 000 klasyfikacji!
|
||||
Przy 100 cls/sec → 3.3 godziny na 1 obraz → NIEPRAKTYCZNE
|
||||

|
||||
|
||||
**Podejście 2 — Region Proposals + Klasyfikator (szybsze):**
|
||||
---
|
||||
|
||||
Selective Search → ~2000 regionów (zamiast milionów)
|
||||
Każdy region → resize → klasyfikator → wynik + NMS
|
||||
Przy 100 cls/sec → 20 sec/obraz → lepiej, ale wciąż wolno
|
||||
To jest dokładnie R-CNN (2014)
|
||||
#### Podejście 1 — Sliding Window (najprostsze, NAJWOLNIEJSZE)
|
||||
|
||||
**Podejście 3 — Fine-tune backbone + detection head (najlepsze):**
|
||||
**Idea:** Wycinaj prostokątne fragmenty obrazu, KAŻDY pokaż klasyfikatorowi, zbierz pozytywne.
|
||||
|
||||
Pretrained classifier (ResNet): obraz → cechy → FC → "kot"
|
||||
Zamień FC na detection head:
|
||||
obraz → cechy (backbone) → [cls head: P(klasa)]
|
||||
→ [bbox head: Δx, Δy, Δw, Δh]
|
||||
Dotrenuj na danych z bounding boxami (COCO, VOC)
|
||||
= Transfer learning → NAJLEPSZA jakość + szybkość
|
||||
To jest Faster R-CNN, YOLO, SSD — wszystkie używają pretrained backbone!
|
||||
**Mnemonik: „WYCINAJ i PYTAJ" — jak wycinanie ciasteczek: koło po kole, aż cały obraz pokryty.**
|
||||
|
||||
Podsumowanie:
|
||||
Sliding Window: ~milion klasyfikacji → NIEPRAKTYCZNE
|
||||
Region Proposals: ~2000 klasyfikacji → wolne ale działa (R-CNN)
|
||||
Fine-tune: 1 przejście sieci → szybkie i dokładne (Faster R-CNN, YOLO)
|
||||

|
||||
|
||||
Pseudokod:
|
||||
def sliding_window_detect(image, classifier, window_size=64, step=8):
|
||||
detections = []
|
||||
for scale in [0.5, 0.75, 1.0, 1.5, 2.0]: # 5 skal
|
||||
resized = resize(image, scale)
|
||||
for y in range(0, resized.height - window_size, step):
|
||||
for x in range(0, resized.width - window_size, step):
|
||||
window = resized[y:y+window_size, x:x+window_size]
|
||||
label, confidence = classifier.predict(window)
|
||||
if label != "tło" and confidence > 0.5:
|
||||
# przelicz współrzędne na oryginał
|
||||
bbox = (x/scale, y/scale,
|
||||
(x+window_size)/scale, (y+window_size)/scale)
|
||||
detections.append((label, bbox, confidence))
|
||||
return nms(detections) # usuń duplikaty
|
||||
|
||||
**Dlaczego wiele skal?** Obiekty mają różne rozmiary — kot blisko = duży, kot daleko = mały. Okno 64×64 nie złapie kota 200×200.
|
||||
|
||||
Obliczenia dla obrazu 640×480:
|
||||
Pozycje na skali 1.0: (640-64)/8 × (480-64)/8 = 72 × 52 = 3 744
|
||||
× 5 skal = 18 720 okien
|
||||
× klasyfikacja ResNet (~10ms/obraz na GPU) = ~3 minuty
|
||||
× na CPU (~100ms/obraz) = ~30 minut na 1 obraz!
|
||||
⚠ NIEPRAKTYCZNE dla zastosowań real-time
|
||||
|
||||
**Wady:** (1) Ekstremalnie wolne. (2) Stały kształt okna — obiekty nie są kwadratowe. (3) ~99.9% okien to „tło" → marnowanie czasu.
|
||||
|
||||
---
|
||||
|
||||
#### Podejście 2 — Region Proposals + Klasyfikator (= R-CNN)
|
||||
|
||||
**Idea:** Zamiast milionów okien, inteligentnie zaproponuj ~2000 regionów, w których MOGĄ być obiekty, i tylko te sklasyfikuj.
|
||||
|
||||
**Mnemonik: „INTELIGENTNE CIĘCIE" — zamiast kroić cały tort na milion kawałków, wytnij tylko tam, gdzie widzisz wiśnie (obiekty).**
|
||||
|
||||
Pseudokod (= R-CNN):
|
||||
def region_proposal_detect(image, classifier):
|
||||
# Krok 1: Selective Search — inteligentnie generuj regiony
|
||||
proposals = selective_search(image) # ~2000 prostokątów
|
||||
detections = []
|
||||
|
||||
# Krok 2: Dla KAŻDEGO regionu — clasificuj
|
||||
for bbox in proposals: # ~2000 iteracji (nie milion!)
|
||||
crop = image[bbox] # wytnij region
|
||||
crop = resize(crop, 224, 224) # rozmiar wymagany przez CNN
|
||||
features = cnn_backbone(crop) # ResNet → wektor 2048 cech
|
||||
label, conf = svm_classify(features) # SVM: "samochód? kot? tło?"
|
||||
if label != "tło" and conf > 0.5:
|
||||
detections.append((label, bbox, conf))
|
||||
|
||||
# Krok 3: bbox regression — doprecyzuj pozycje
|
||||
for det in detections:
|
||||
det.bbox += bbox_regressor(det.features) # Δx, Δy, Δw, Δh
|
||||
|
||||
return nms(detections) # Krok 4: usuń duplikaty
|
||||
|
||||
**Dlaczego 2000 a nie milion?** Selective Search łączy podobne fragmenty obrazu (kolor, tekstura) bottom-up. Wynik: ~2000 „mądrych" propozycji, z których ~50% zawiera coś (vs 0.1% w sliding window).
|
||||
|
||||
Porównanie z sliding window:
|
||||
Sliding Window: ~18 000 okien × 10ms = ~3 min
|
||||
Proposals: ~2 000 regionów × 10ms = ~20 sec ← 9× szybciej
|
||||
ALE wciąż 2000 × forward pass CNN → dlatego powstał Fast R-CNN!
|
||||
|
||||
**Wady:** (1) Selective Search jest osobnym algorytmem (nie end-to-end). (2) 2000 × forward pass CNN = wciąż wolno. (3) SVM trenowany OSOBNO od CNN.
|
||||
|
||||
---
|
||||
|
||||
#### Podejście 3 — Fine-tune backbone + detection head (NAJLEPSZE)
|
||||
|
||||
**Idea:** Weź pretrenowany klasyfikator, ODETNIJ głowicę klasyfikacyjną (FC 1000 klas), zastąp ją DWOMA nowymi głowicami: (1) głowica klasyfikacji → klasa obiektu, (2) głowica regresji → pozycja bbox.
|
||||
|
||||
**Mnemonik: „PRZESZCZEP GŁOWY" — ten sam silnik (backbone), nowa głowa (detection head).**
|
||||
|
||||
Pseudokod (= Faster R-CNN / YOLO w uproszczeniu):
|
||||
# KROK 1: Weź pretrenowany klasyfikator
|
||||
resnet = load_pretrained("resnet50_imagenet") # 1000 klas ImageNet
|
||||
|
||||
# KROK 2: Odetnij starą głowicę klasyfikacji
|
||||
backbone = resnet.layers[:-2] # ZACHOWAJ: Conv1...Conv5 (ekstraktor cech)
|
||||
# WYRZUĆ: FC(1000) + Softmax
|
||||
|
||||
# KROK 3: Dodaj nowe głowice detekcji
|
||||
class DetectionHead:
|
||||
def __init__(self):
|
||||
self.cls_head = Linear(2048, num_classes) # "samochód? kot? tło?"
|
||||
self.bbox_head = Linear(2048, 4) # Δx, Δy, Δw, Δh
|
||||
|
||||
def forward(self, features):
|
||||
cls = softmax(self.cls_head(features)) # P(klasa)
|
||||
bbox = self.bbox_head(features) # przesunięcie bbox
|
||||
return cls, bbox
|
||||
|
||||
# KROK 4: Zamroź backbone, trenuj głowice na danych detekcyjnych
|
||||
for image, gt_boxes, gt_labels in coco_dataset:
|
||||
features = backbone(image) # pretrenowane cechy (zamrożone)
|
||||
cls, bbox = detection_head(features)
|
||||
loss = cls_loss(cls, gt_labels) + bbox_loss(bbox, gt_boxes)
|
||||
loss.backward() # aktualizuj TYLKO detection_head
|
||||
|
||||
# KROK 5 (opcja): Fine-tune — odmroź backbone z MAŁYM learning rate
|
||||
backbone.unfreeze()
|
||||
optimizer = SGD(lr=0.0001) # 10× mniejszy niż dla głowicy!
|
||||
# trenuj jak w kroku 4, ale teraz backbone też się uczy
|
||||
|
||||
**Dlaczego to działa?** Pretrenowany backbone na ImageNet „wie", jak wyglądają krawędzie, tekstury, kształty. Te cechy są UNIWERSALNE — przydają się zarówno do klasyfikacji „złota rybka vs samolot" jak i do detekcji „samochód na zdjęciu z drona".
|
||||
|
||||
Transfer learning w liczbach:
|
||||
Trenowanie od zera na COCO (330K obrazów): ~12h na 8×V100 GPU
|
||||
Fine-tune pretrained ResNet-50: ~4h na 8×V100 GPU ← 3× szybciej!
|
||||
Fine-tune osiąga mAP ~42%, od zera ~38% ← lepsze wyniki!
|
||||
|
||||
**Pełny przykład w PyTorch (Faster R-CNN z pretrained backbone):**
|
||||
|
||||
import torchvision
|
||||
from torchvision.models.detection import fasterrcnn_resnet50_fpn
|
||||
|
||||
# Gotowy detektor z pretrained backbone!
|
||||
model = fasterrcnn_resnet50_fpn(pretrained=True)
|
||||
|
||||
# Custom: zmiana na 5 klas (zamiast 91 COCO)
|
||||
num_classes = 5 # 4 obiekty + tło
|
||||
in_features = model.roi_heads.box_predictor.cls_score.in_features
|
||||
model.roi_heads.box_predictor = FastRCNNPredictor(in_features, num_classes)
|
||||
|
||||
# Trening:
|
||||
model.train()
|
||||
for images, targets in dataloader:
|
||||
loss_dict = model(images, targets) # cls_loss + bbox_loss
|
||||
total_loss = sum(loss_dict.values())
|
||||
total_loss.backward()
|
||||
optimizer.step()
|
||||
|
||||
# Inferencja:
|
||||
model.eval()
|
||||
predictions = model([test_image])
|
||||
# predictions = [{'boxes': tensor, 'labels': tensor, 'scores': tensor}]
|
||||
# boxes = [[x1,y1,x2,y2], ...], labels = [1, 3, ...], scores = [0.95, 0.88, ...]
|
||||
|
||||
---
|
||||
|
||||
#### Podsumowanie — porządek od NAJGORSZEGO do NAJLEPSZEGO:
|
||||
|
||||
Podejście Okien Czas/obraz Jakość Rok Przykład
|
||||
──────────────────────────────────────────────────────────────────────────
|
||||
Sliding Window ~milion ~30 min niska - (teoria)
|
||||
Region Proposals ~2000 ~20-50 sec średnia 2014 R-CNN
|
||||
Fine-tune + RPN ~300 ~0.2 sec wysoka 2015 Faster R-CNN
|
||||
One-stage 1×siatka ~7-22 ms wysoka 2016+ YOLO, SSD
|
||||
Transformer N queries ~25 ms wysoka 2020 DETR
|
||||
|
||||
Mnemonik porządku: „SRFTD" = „Sliding → Region → Fine-tune → Transformer → (Done!)"
|
||||
= „Szukaj Ręcznie, Finalnie Transformer (Detekuje!)"
|
||||
|
||||
---
|
||||
|
||||
### NMS (Non-Maximum Suppression) — post-processing
|
||||
|
||||

|
||||
|
||||
Detektor generuje WIELE nakładających się bbox dla jednego obiektu:
|
||||
[bbox1, 0.95], [bbox2, 0.90], [bbox3, 0.85] — wszystkie na tym samym kocie
|
||||
|
||||
Algorytm NMS:
|
||||
1. Sortuj po confidence: [0.95, 0.90, 0.85]
|
||||
2. Weź najlepszą (0.95) → ZACHOWAJ
|
||||
3. Oblicz IoU z resztą: IoU(bbox1,bbox2)=0.82, IoU(bbox1,bbox3)=0.75
|
||||
4. Usuń te z IoU > próg (0.5): usuń bbox2 i bbox3
|
||||
5. Powtórz dla następnej najlepszej
|
||||
Wynik: 1 bbox per obiekt
|
||||
Pseudokod NMS:
|
||||
def nms(detections, iou_threshold=0.5):
|
||||
detections.sort(by=confidence, descending=True)
|
||||
keep = []
|
||||
while detections:
|
||||
best = detections.pop(0) # weź najlepszą
|
||||
keep.append(best) # ZACHOWAJ
|
||||
detections = [d for d in detections
|
||||
if iou(best, d) < iou_threshold] # usuń nakładające
|
||||
return keep
|
||||
|
||||
Krok po kroku (przykład):
|
||||
1. Sortuj: [0.95, 0.90, 0.85, 0.40]
|
||||
2. Weź bbox₁ (0.95) → ZACHOWAJ
|
||||
3. IoU(bbox₁, bbox₂) = 0.82 > 0.5 → USUŃ (duplikat!)
|
||||
IoU(bbox₁, bbox₃) = 0.75 > 0.5 → USUŃ (duplikat!)
|
||||
IoU(bbox₁, bbox₄) = 0.10 < 0.5 → ZACHOWAJ (INNY obiekt!)
|
||||
4. Wynik: [bbox₁, bbox₄] — 2 unikalne obiekty
|
||||
|
||||

|
||||
|
||||
Mnemonik NMS: „Najlepszy Ma Się dobrze" — zachowaj najlepszą, resztę wyrzuć
|
||||
Mnemonik IoU: „Ile pokrycia Ustalono?" — pole(∩) / pole(A∪B)
|
||||
|
||||
### Etymologia
|
||||
|
||||
@ -832,10 +1020,15 @@ Masz wytrenowany klasyfikator (np. ResNet na ImageNet: obraz → „kot"). Jak g
|
||||
- **FC = „Full Connection"** — każdy z każdym, warstwa decyzyjna na końcu CNN
|
||||
- **Backbone = SILNIK samochodu** — ten sam silnik (ResNet), różne karoserie (klasyfikacja/detekcja/segmentacja)
|
||||
- **Backbone'y: A→V→R = „Architektura Bardzo Rezylientna"** — AlexNet (2012) → VGG (2014) → ResNet (2015)
|
||||
- **Transfer learning** — nie ucz się od zera, przenieś wiedzę z ImageNet
|
||||
- **Viola-Jones: kaskada = „SITO"** — piach odpada wcześnie, złoto (twarz) zostaje na końcu
|
||||
- **Transfer learning = „PRZESZCZEP GŁOWY"** — nie ucz się od zera, przenieś wiedzę z ImageNet, zmień głowicę
|
||||
- **HOG kroki: „GOKBN" = „Grasz Ostro, Kumplu? Bądź Naturalny"** — Gradienty → Orientacja → Komórki → Bloki → Normalizacja
|
||||
- **SVM = „LINIA MAKSYMALNEGO ODDECHU"** — margines jak most: im szerszy, tym bezpieczniej
|
||||
- **Viola-Jones: „HIC" = Haar + Integral Image + Cascade**
|
||||
- **Haar = „Hej, A tu jest Różnica?"** — porównuje jasne i ciemne prostokąty
|
||||
- **Integral Image = „4 Odczyty I Gotowe" (4OIG)** — suma dowolnego prostokąta O(1)
|
||||
- **Kaskada = „SITO"** — piach odpada wcześnie, złoto (twarz) zostaje na końcu
|
||||
- **Viola-Jones pipeline: „SIKN" = „Szybko Identyfikuj Kształty Niezwykłe"** — Sliding → Integral → Kaskada → NMS
|
||||
- **AdaBoost = „ADAptacyjnie BOOSTuj"** — słabe modele razem = silny
|
||||
- **Integral Image** — 4 odczyty = suma dowolnego prostokąta, zawsze O(1)
|
||||
- **Selective Search** — inteligentne łączenie regionów zamiast milionów okien
|
||||
- **ROI Pooling** — dowolny rozmiar → stały rozmiar (siatkowanie + max)
|
||||
- **Bbox regression = „GPS korekta"** — popraw przybliżoną pozycję o Δx, Δy, Δw, Δh
|
||||
@ -843,6 +1036,7 @@ Masz wytrenowany klasyfikator (np. ResNet na ImageNet: obraz → „kot"). Jak g
|
||||
- **YOLO = „You Only Look Once"** — jednoetapowy, szybki, siatka S×S
|
||||
- **Faster R-CNN = CNN + RPN + ROI Pool** — dwuetapowy, dokładny
|
||||
- **NMS = „Najlepszy Ma Się dobrze"** — zachowaj najlepszą detekcję, usuń duplikaty
|
||||
- **IoU = „Ile pokrycia Ustalono?"** — pole(∩) / pole(A∪B)
|
||||
- **DETR = „Detekcja Eliminująca Trikowe Redundancje"** — bez NMS, bez anchorów, transformer
|
||||
- **Detektor z klasyfikatora:** sliding window (wolno) → proposals (lepiej) → fine-tune backbone (najlepiej) → DETR (najprościej)
|
||||
- **Detektor z klasyfikatora: „SRF" = „Szukaj Ręcznie, Finalnie optymalizuj!"** — Sliding Window (wolno) → Region Proposals (lepiej) → Fine-tune backbone (najlepiej)
|
||||
|
||||
|
||||
@ -61,15 +61,16 @@
|
||||
│ → NIE → wróć do 1│
|
||||
└──────────────────────────────────────────┘
|
||||
|
||||
**Metody interaktywne (interactive methods)** — konkretne algorytmy realizujące interaktywne wspomaganie decyzji. W kontekście tego pytania są to: metoda loterii (wyznaczanie funkcji użyteczności U(x) przez pytania o loterie), metoda certainty equivalent (wyznaczanie ekwiwalentu pewności), AHP (porównania parami), PROMETHEE i ELECTRE (metody outranking). Każda z nich wymaga od decydenta ODPOWIEDZI na pytania — to czyni je interaktywnymi.
|
||||
**Metody interaktywne (interactive methods)** — konkretne algorytmy realizujące interaktywne wspomaganie decyzji. W kontekście tego pytania są to KRYTERIA DECYZYJNE stosowane gdy decydent nie zna prawdopodobieństw stanów natury (lub je zakłada). Interaktywność polega na tym, że decydent WYBIERA kryterium (a w przypadku Hurwicza — także parametr α), co wymaga dialogu o jego postawie wobec ryzyka.
|
||||
|
||||
Metoda Jakie pytania zadaje decydentowi?
|
||||
Kryterium Pytanie do decydenta / założenie
|
||||
──────────────────────────────────────────────────────────────────
|
||||
Loteria „Wolisz X na pewno, czy loterię (p: best, 1-p: worst)?"
|
||||
CE „Ile na pewno = ta loteria?"
|
||||
AHP „Ile razy kryterium A ważniejsze od B?" (skala 1-9)
|
||||
PROMETHEE „Jak ważne jest każde kryterium?" (wagi)
|
||||
ELECTRE „Jaki próg zgody/sprzeciwu?"
|
||||
Wart. oczekiwana „Znasz prawdopodobieństwa stanów?" (potrzebne p)
|
||||
Laplace'a „Każdy stan natury równie prawdopodobny" (założenie)
|
||||
Optymistyczne „Zawsze liczysz na najlepszy scenariusz?" (postawa)
|
||||
Pesymistyczne „Chcesz zabezpieczyć się przed najgorszym?" (postawa)
|
||||
Hurwicza „Podaj swój współczynnik optymizmu α ∈ [0,1]" (parametr)
|
||||
Savage'a „Chcesz minimalizować żal z podjętej decyzji?" (postawa)
|
||||
|
||||
---
|
||||
|
||||
@ -81,101 +82,234 @@
|
||||
|
||||
Przykład ryzyka: „Z 60% szansą zysk 100 zł, z 40% strata 50 zł." Przykład niepewności: „Możemy zyskać lub stracić, ale nie wiemy ile i z jakim prawdopodobieństwem."
|
||||
|
||||
---
|
||||
|
||||
**Decydent (decision maker)** — osoba lub podmiot, który musi wybrać jedną z dostępnych alternatyw. Metody interaktywne wymagają dialogu z decydentem — pytamy go o preferencje, zamiast zakładać je z góry.
|
||||
|
||||
**Funkcja użyteczności U(x) (utility function)** — matematyczne przypisanie „wartości subiektywnej" do wyniku. Dla kogoś, kto boi się ryzyka, różnica między 0 a 1000 zł jest bardziej odczuwalna niż między 9000 a 10000 zł.
|
||||
|
||||
U(x)
|
||||
│ ╭──────── wklęsła (risk-averse)
|
||||
│ ╱╱
|
||||
│ ╱╱
|
||||
│╱╱
|
||||
└──────────── x (pieniądze)
|
||||
|
||||
**Risk averse (awersja do ryzyka)** — decydent preferuje pewne wyniki nad ryzykowne loterie o tej samej wartości oczekiwanej. Funkcja U jest **wklęsła** (concave): U''(x) < 0.
|
||||
|
||||
Loteria: 50% szans na 0 zł, 50% na 100 zł → E[X] = 50 zł
|
||||
Risk-averse: „Wolę 50 zł na pewno" (a nawet 40 zł na pewno!)
|
||||
|
||||
**Risk neutral (neutralność)** — U jest liniowa. Decydentowi jest obojętne czy dostanie E[X] na pewno, czy zagra w loterię.
|
||||
|
||||
**Risk seeking (skłonność do ryzyka)** — U jest **wypukła** (convex). Decydent woli ryzyko niż pewny E[X]. „Wolę zagrać niż dostać pewniaka."
|
||||

|
||||
|
||||
---
|
||||
|
||||
**Loteria (lottery)** — formalizacja decyzji ryzykownej: zbiór wyników z ich prawdopodobieństwami. Notacja L = (p: best, 1-p: worst).
|
||||
**Decydent (decision maker)** — osoba lub podmiot, który musi wybrać jedną z dostępnych alternatyw. Metody interaktywne wymagają dialogu z decydentem — pytamy go o postawę wobec ryzyka (optymista? pesymista?) i ew. parametry (α Hurwicza).
|
||||
|
||||
L = (0.6: 100 zł, 0.4: 0 zł)
|
||||
E[L] = 0.6 × 100 + 0.4 × 0 = 60 zł
|
||||
**Stan natury (state of nature)** — scenariusz/sytuacja zewnętrzna, na którą decydent NIE ma wpływu. Np. pogoda, koniunktura gospodarcza, zachowanie konkurencji. Oznaczamy S₁, S₂, …, Sₙ.
|
||||
|
||||
**Metoda loterii (lottery method)** — technika wyznaczania U(x) przez zadawanie pytań decydentowi. Ustalamy U(worst)=0, U(best)=1 i szukamy „indifference point" — prawdopodobieństwa p*, przy którym decydent jest obojętny między pewną kwotą a loterią.
|
||||
Stany natury: S₁ = „dobra koniunktura", S₂ = „zła koniunktura"
|
||||
Decydent NIE wybiera stanu — stan „się zdarza".
|
||||
|
||||
Pyt: „Wolisz 500 zł na pewno, czy loterię (p: 1000 zł, 1-p: 0 zł)?"
|
||||
Jeśli punkt obojętności p* = 0.7 → U(500) = 0.7
|
||||
(Risk-neutral dałby p*=0.5, bo 500/1000=0.5)
|
||||
**Macierz wypłat (payoff matrix)** — tabela, w której wiersze = alternatywy (decyzje), kolumny = stany natury, a komórki = wyniki (wypłaty). To podstawowa struktura danych dla WSZYSTKICH kryteriów decyzyjnych.
|
||||
|
||||
**Certainty Equivalent (CE, ekwiwalent pewności)** — pewna kwota, która jest dla decydenta równoważna danej loterii.
|
||||
Przykład — macierz wypłat (zyski w tys. zł):
|
||||
S₁ (dobra) S₂ (średnia) S₃ (zła)
|
||||
─────────────────────────────────────────────────────
|
||||
A₁ (fabryka) 200 50 −100
|
||||
A₂ (sklep) 80 70 40
|
||||
A₃ (obligacje) 30 30 30
|
||||
|
||||
Loteria: 50/50 zysk 100 zł lub 0 zł → E[X] = 50 zł
|
||||
Decydent risk-averse: CE = 35 zł (wolałby 35 zł na pewno niż grać)
|
||||
Risk premium = E[X] − CE = 50 − 35 = 15 zł
|
||||
A₁ może dać 200k, ale też stratę 100k.
|
||||
A₃ daje 30k niezależnie od stanu → decyzja bezpieczna.
|
||||
|
||||
**Wartość oczekiwana E[X] (expected value)** — średni wynik loterii ważony prawdopodobieństwami.
|
||||
**Wartość oczekiwana E[X] (expected value)** — średni wynik ważony prawdopodobieństwami stanów natury. Używana w kryterium wartości oczekiwanej (gdy znamy prawdopodobieństwa) i w kryterium Laplace'a (z równymi prawdopodobieństwami).
|
||||
|
||||
E[X] = Σ pᵢ × xᵢ
|
||||
Dla L = (0.3: 100, 0.7: 20): E[X] = 0.3×100 + 0.7×20 = 44
|
||||
|
||||
Przykład z PRAWDZIWYMI prawdopodobieństwami (p₁=0.5, p₂=0.3, p₃=0.2):
|
||||
E[A₁] = 0.5×200 + 0.3×50 + 0.2×(−100) = 100 + 15 − 20 = 95 ← MAX
|
||||
E[A₂] = 0.5×80 + 0.3×70 + 0.2×40 = 40 + 21 + 8 = 69
|
||||
E[A₃] = 0.5×30 + 0.3×30 + 0.2×30 = 15 + 9 + 6 = 30
|
||||
|
||||
Dla Laplace'a (równe prawdopodobieństwa, p₁ = p₂ = p₃ = 1/3):
|
||||
E[A₁] = (200 + 50 + (−100)) / 3 = 150/3 = 50
|
||||
E[A₂] = (80 + 70 + 40) / 3 = 190/3 ≈ 63.3 ← najlepsza wg Laplace'a
|
||||
E[A₃] = (30 + 30 + 30) / 3 = 30
|
||||
|
||||
**Kryterium wartości oczekiwanej (expected value criterion)** — NAJPROSTSZA metoda decyzyjna W WARUNKACH RYZYKA (gdy znamy prawdopodobieństwa). Oblicz E[Aᵢ] = Σⱼ pⱼ × aᵢⱼ dla każdej alternatywy i wybierz tę z NAJWYŻSZĄ wartością oczekiwaną.
|
||||
|
||||
Formuła: V(Aᵢ) = Σⱼ pⱼ × aᵢⱼ → wybierz Aᵢ z max V(Aᵢ)
|
||||
|
||||
Przykład (p₁=0.5, p₂=0.3, p₃=0.2):
|
||||
V(A₁) = 0.5×200 + 0.3×50 + 0.2×(−100) = 95 ← MAX → wybieramy A₁
|
||||
V(A₂) = 0.5×80 + 0.3×70 + 0.2×40 = 69
|
||||
V(A₃) = 0.5×30 + 0.3×30 + 0.2×30 = 30
|
||||
|
||||
Kluczowa różnica od Laplace'a:
|
||||
- Laplace: ZAKŁADA p = 1/n (bo nie znamy prawdopodobieństw)
|
||||
- Wart. oczekiwana: UŻYWA PRAWDZIWYCH p (bo je znamy!)
|
||||
|
||||
Przykład życiowy: firma rozważa inwestycję
|
||||
- Analityk oszacował: P(boom) = 50%, P(stabilna) = 30%, P(kryzys) = 20%
|
||||
- Fabryka wygrywa (E=95k), bo wysoki zysk w boomie (200k) × duże p (50%)
|
||||
przeważa nad stratą w kryzysie (−100k) × małe p (20%)
|
||||
|
||||
Ograniczenie: E[X] ignoruje ROZRZUT wyników! A₁ ma E=95k, ale może
|
||||
dać −100k. Decydent z awersją do ryzyka może wolę A₂ (E=69k, ale
|
||||
minimum 40k). Dlatego sam E[X] nie wystarczy — potrzeba też analizy
|
||||
ryzyka (np. wariancji, worst-case).
|
||||
|
||||
Mnemonik: „Średnia ważona — jak średnia ocen"
|
||||
Wynik × prawdopodobieństwo = waga.
|
||||
Sumuj wagi → E[X]. Jak w dzienniku: 5×0.3 + 4×0.5 + 2×0.2 = 3.9
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
**AHP (Analytic Hierarchy Process)** — metoda Saaty'ego do wyboru najlepszej alternatywy gdy mamy wiele kryteriów. Rozbija problem na hierarchię: Cel → Kryteria → Alternatywy.
|
||||
**Kryterium decyzyjne (decision criterion)** — reguła/algorytm, który z macierzy wypłat wyznacza „najlepszą" alternatywę. Każde kryterium odzwierciedla INNĄ postawę decydenta wobec ryzyka. Dlatego to samo zadanie może dać INNE odpowiedzi zależnie od wybranego kryterium — i to jest OK.
|
||||
|
||||
Cel: Wybierz samochód
|
||||
├── Kryterium: Cena
|
||||
│ ├── Auto A, Auto B, Auto C
|
||||
├── Kryterium: Komfort
|
||||
│ ├── Auto A, Auto B, Auto C
|
||||
└── Kryterium: Spalanie
|
||||
├── Auto A, Auto B, Auto C
|
||||
Te same dane, różne kryteria → różne „najlepsze" decyzje:
|
||||
Kryterium Wygrywa Dlaczego?
|
||||
─────────────────────────────────────────────────────
|
||||
Wart. oczekiwana A₁ (95) najwyższa E[X] z prawdziwymi p
|
||||
Laplace A₂ (≈63) najwyższa średnia (równe p)
|
||||
Optymistyczne A₁ (200) najwyższy max
|
||||
Pesymistyczne A₂ (40) najwyższy min (bezpieczne)
|
||||
Hurwicz (α=0.6) A₁ (80) kompromis
|
||||
Savage A₂ (120) najniższy max żalu
|
||||
|
||||
**Porównania parami (pairwise comparisons)** — w AHP porównujemy każdą parę kryteriów/alternatyw i oceniamy na skali 1-9 Saaty'ego:
|
||||

|
||||
|
||||
1 = równe znaczenie
|
||||
3 = umiarkowana przewaga
|
||||
5 = silna przewaga
|
||||
7 = bardzo silna
|
||||
9 = absolutna przewaga
|
||||
**Kryterium Laplace'a (Laplace criterion / principle of insufficient reason)** — zakładamy, że WSZYSTKIE stany natury są RÓWNIE PRAWDOPODOBNE (bo nie mamy powodu faworyzować żadnego). Obliczamy średnią arytmetyczną wypłat dla każdej alternatywy i wybieramy najwyższą.
|
||||
|
||||
Macierz 3×3 (Cena vs Komfort vs Spalanie):
|
||||
Cena Komf Spal
|
||||
Cena [ 1 3 5 ]
|
||||
Komf [ 1/3 1 2 ]
|
||||
Spal [ 1/5 1/2 1 ]
|
||||
Formuła: V(Aᵢ) = (1/n) × Σⱼ aᵢⱼ (n = liczba stanów natury)
|
||||
|
||||
**Eigenvalue (wartość własna)** — z macierzy porównań wyznaczamy wektor własny → wagi kryteriów. To serce AHP: macierz parami → ranking numeryczny.
|
||||
Przykład z macierzy powyżej (n=3):
|
||||
V(A₁) = (200 + 50 + (−100)) / 3 = 50.0
|
||||
V(A₂) = (80 + 70 + 40) / 3 = 63.3 ← MAX → wybieramy A₂
|
||||
V(A₃) = (30 + 30 + 30) / 3 = 30.0
|
||||
|
||||
**Consistency Ratio (CR)** — miara spójności ocen decydenta. Jeśli A>B i B>C, ale C>A, to niespójne. CR < 0.1 = akceptowalne. CR ≥ 0.1 → decydent powinien poprawić oceny.
|
||||
Interaktywność: decydent musi zaakceptować założenie równych
|
||||
prawdopodobieństw — „Czy zgadzasz się, że każdy scenariusz
|
||||
jest tak samo możliwy?"
|
||||
|
||||
Przykład życiowy: wybieram restaurację w nieznanym mieście.
|
||||
Nie wiem, która dobra — traktuję je „po równo" i porównuję
|
||||
średnią ocen z 3 portali (każdy portal = stan natury z p=1/3).
|
||||
|
||||
Mnemonik: „Laplace = Loteria — Losowe, ALE Po równo"
|
||||
|
||||
**Kryterium optymistyczne (maximax / optimistic criterion)** — decydent-OPTYMISTA: dla każdej alternatywy bierzemy NAJLEPSZY możliwy wynik (max w wierszu), potem wybieramy alternatywę z najwyższym z tych maksimów.
|
||||
|
||||
Formuła: V(Aᵢ) = maxⱼ aᵢⱼ → wybierz Aᵢ z max V(Aᵢ)
|
||||
|
||||
max(A₁) = max(200, 50, −100) = 200 ← MAX → wybieramy A₁
|
||||
max(A₂) = max(80, 70, 40) = 80
|
||||
max(A₃) = max(30, 30, 30) = 30
|
||||
|
||||
A₁ wygrywa — optymista liczy na najlepszy scenariusz (200k).
|
||||
Ryzyko: jeśli S₃, to strata −100k!
|
||||
|
||||
Przykład życiowy: gracz w pokera, który zawsze idzie all-in,
|
||||
bo „może trafię straight flush". Patrzy TYLKO na najlepsze
|
||||
możliwe rozdanie. Ignoruje szansę przegranej.
|
||||
|
||||
Mnemonik: „Maximax = Marzyciel — Max z Max, bo MARZĘ o najlepszym"
|
||||
|
||||
**Kryterium pesymistyczne (maximin / Wald criterion)** — decydent-PESYMISTA: dla każdej alternatywy bierzemy NAJGORSZY możliwy wynik (min w wierszu), potem wybieramy alternatywę z najwyższym z tych minimów. Zabezpieczamy się przed najgorszym scenariuszem.
|
||||
|
||||
Formuła: V(Aᵢ) = minⱼ aᵢⱼ → wybierz Aᵢ z max V(Aᵢ)
|
||||
|
||||
min(A₁) = min(200, 50, −100) = −100
|
||||
min(A₂) = min(80, 70, 40) = 40
|
||||
min(A₃) = min(30, 30, 30) = 30
|
||||
|
||||
max{−100, 40, 30} = 40 → wybieramy A₂
|
||||
Pesymista: „Nawet w najgorszym razie dostanę 40k" (A₂ jest bezpieczna).
|
||||
|
||||
Przykład życiowy: jadąc na wakacje, pesymista wybiera hotel z gwarancją
|
||||
zwrotu, bo „a jeśli będzie brzydka pogoda?". Woli gwarantowany minimum
|
||||
komfort niż ryzykować. Ubezpieczenia działają na tej zasadzie.
|
||||
|
||||
Mnemonik: „Maximin = Mur obronny — buduję MUR pod MINimum, bo zawsze
|
||||
zakładam NAJGORSZE (Wald = Wall = Mur)"
|
||||
|
||||
**Kryterium Hurwicza (Hurwicz criterion)** — kompromis między optymizmem a pesymizmem. Decydent podaje współczynnik optymizmu α ∈ [0, 1], gdzie α = 1 to pełny optymista, α = 0 to pełny pesymista.
|
||||
|
||||
Formuła: V(Aᵢ) = α × maxⱼ aᵢⱼ + (1−α) × minⱼ aᵢⱼ
|
||||
|
||||
Dla α = 0.6:
|
||||
V(A₁) = 0.6×200 + 0.4×(−100) = 120 − 40 = 80
|
||||
V(A₂) = 0.6×80 + 0.4×40 = 48 + 16 = 64
|
||||
V(A₃) = 0.6×30 + 0.4×30 = 18 + 12 = 30
|
||||
|
||||
max{80, 64, 30} = 80 → A₁ wygrywa dla α=0.6.
|
||||
|
||||
Dla α = 0.3 (bardziej pesymistyczny):
|
||||
V(A₁) = 0.3×200 + 0.7×(−100) = 60 − 70 = −10
|
||||
V(A₂) = 0.3×80 + 0.7×40 = 24 + 28 = 52 ← teraz A₂!
|
||||
V(A₃) = 0.3×30 + 0.7×30 = 9 + 21 = 30
|
||||
|
||||
→ Zmiana α zmienia wynik! Dlatego TO kryterium jest najbardziej
|
||||
interaktywne — decydent MUSI podać swoje α w dialogu.
|
||||
|
||||
Przypadki specjalne:
|
||||
α = 1 → kryterium optymistyczne (maximax)
|
||||
α = 0 → kryterium pesymistyczne (maximin)
|
||||
|
||||
Przykład życiowy: kupujesz akcje. Z α=0.8 (optymista) patrzysz głównie
|
||||
na potencjalny zysk. Z α=0.2 (pesymista) prawie tylko na potencjalną
|
||||
stratę. α to „pokrętło optymizmu" — kręcisz i widzisz jak zmienia
|
||||
się rekomendacja.
|
||||
|
||||
Mnemonik: „Hurwicz = Huśtawka — huśtasz się między max a min,
|
||||
α mówi jak daleko w stronę max się wychylasz"
|
||||
|
||||

|
||||
|
||||
**Współczynnik optymizmu α (optimism coefficient)** — parametr Hurwicza z przedziału [0, 1]. Wyraża postawę decydenta: α bliskie 1 = optymista (wierzy w dobre scenariusze), α bliskie 0 = pesymista.
|
||||
|
||||
α = 1.0 → patrzę tylko na max → maximax
|
||||
α = 0.5 → równa waga max i min
|
||||
α = 0.0 → patrzę tylko na min → maximin
|
||||
|
||||
---
|
||||
|
||||
**PROMETHEE (Preference Ranking Organization METHod for Enrichment Evaluations)** — metoda porównująca alternatywy parami per kryterium za pomocą funkcji preferencji. Wynik: przepływy (flows).
|
||||
**Macierz żalu / macierz strat (regret matrix)** — tabela, w której każda komórka zawiera ŻALE (regret) = ile TRACĘ wybierając daną alternatywę zamiast najlepszej w danym stanie natury.
|
||||
|
||||
Φ⁺(a) = outgoing flow = „o ile a jest lepsze od reszty" (siła)
|
||||
Φ⁻(a) = incoming flow = „o ile reszta jest lepsza od a" (słabość)
|
||||
Φ(a) = Φ⁺(a) − Φ⁻(a) = net flow → im wyższe, tym lepsza alternatywa
|
||||
Obliczanie: rᵢⱼ = maxₖ aₖⱼ − aᵢⱼ (max w kolumnie minus wartość w komórce)
|
||||
|
||||
**ELECTRE (ÉLimination Et Choix Traduisant la REalité)** — metoda outranking: A przewyższa B (A S B) gdy:
|
||||
Macierz wypłat: Macierz żalu:
|
||||
S₁ S₂ S₃ S₁ S₂ S₃ max żalu
|
||||
A₁ 200 50 −100 A₁ 0 20 140 140
|
||||
A₂ 80 70 40 A₂ 120 0 0 120 ← MIN
|
||||
A₃ 30 30 30 A₃ 170 40 10 170
|
||||
|
||||
1. **Concordance (zgoda):** wystarczająco dużo kryteriów popiera A nad B
|
||||
2. **Discordance (sprzeciw):** żadne kryterium nie daje B drastycznej przewagi nad A
|
||||
maxₖ aₖ₁ = 200, maxₖ aₖ₂ = 70, maxₖ aₖ₃ = 40
|
||||
r₁₁ = 200−200 = 0, r₁₂ = 70−50 = 20, r₁₃ = 40−(−100) = 140
|
||||
r₂₁ = 200−80 = 120, r₂₂ = 70−70 = 0, r₂₃ = 40−40 = 0
|
||||
r₃₁ = 200−30 = 170, r₃₂ = 70−30 = 40, r₃₃ = 40−30 = 10
|
||||
|
||||
Cecha AHP PROMETHEE ELECTRE
|
||||
──────────────────────────────────────────────────────────
|
||||
Input parami (skala) per-kryterium per-kryterium
|
||||
Wynik wagi + ranking przepływy Φ relacja outranking
|
||||
Typ kompensacyjna częściowo komp. niekompensacyjna
|
||||
Sens wartość globalna przepływ netto eliminacja słabych
|
||||
**Kryterium Savage'a (minimax regret / Savage criterion)** — minimalizacja MAKSYMALNEGO ŻALU. Dla każdej alternatywy znajdujemy największy żal (max w wierszu macierzy żalu), potem wybieramy alternatywę z NAJMNIEJSZYM max żalem.
|
||||
|
||||
Formuła: V(Aᵢ) = maxⱼ rᵢⱼ → wybierz Aᵢ z min V(Aᵢ)
|
||||
|
||||
max żalu(A₁) = max(0, 20, 140) = 140
|
||||
max żalu(A₂) = max(120, 0, 0) = 120 ← MIN → wybieramy A₂
|
||||
max żalu(A₃) = max(170, 40, 10) = 170
|
||||
|
||||
Interpretacja: „Niezależnie co się zdarzy, mój żal nie przekroczy 120k"
|
||||
(gdybym wybrał A₁, mógłbym żałować aż 140k; A₃ → aż 170k).
|
||||
|
||||
Przykład życiowy: wybieram studia. Po 5 latach zobaczę, jaki zawód
|
||||
najlepiej zarabia. Żal = „ile bym zarobił na najlepszych studiach
|
||||
minus ile zarabiam". Savage minimalizuje ten maksymalny żal —
|
||||
wybieram studia, po których NIGDY nie będę żałować za bardzo.
|
||||
|
||||
Mnemonik: „Savage = Szał żalu — Savage to dziki (savage) żal,
|
||||
więc go minimalizuję. Min z max żalu = trzymam żal na smyczy."
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
**Porównanie kryteriów — tabela zbiorcza:**
|
||||
|
||||
Kryterium Postawa Formuła Wymaga od decydenta
|
||||
──────────────────────────────────────────────────────────────────────────────
|
||||
Wart. oczekiw. racjonalna Σ pⱼ·aᵢⱼ podanie prawdopodobieństw
|
||||
Laplace neutralna średnia wypłat akceptacja równych p
|
||||
Optymistyczne optymista max z max nic (automatyczne)
|
||||
Pesymistyczne pesymista max z min nic (automatyczne)
|
||||
Hurwicza kompromis α·max + (1−α)·min podanie α ∈ [0,1]
|
||||
Savage'a minimalizacja min z max żalu nic (automatyczne)
|
||||
żalu
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
@ -183,29 +317,30 @@ Przykład ryzyka: „Z 60% szansą zysk 100 zł, z 40% strata 50 zł." Przykład
|
||||
|
||||
### Interaktywność = dialog z decydentem → odkrycie preferencji (funkcji użyteczności)
|
||||
|
||||
### Metody
|
||||
### Metody (kryteria decyzyjne)
|
||||
|
||||
**1. Metoda loterii:** Ustal U(worst)=0, U(best)=1. Pytaj: „Wolisz x_mid na pewno, czy loterię (p: best, 1-p: worst)?" Punkt obojętności p* = U(x_mid).
|
||||
**0. Kryterium wartości oczekiwanej (E[X]):** WYMAGA prawdopodobieństw stanów (warunki RYZYKA). Oblicz E[Aᵢ] = Σⱼ pⱼ·aᵢⱼ. Wybierz max. Ograniczenie: ignoruje rozrzut/ryzyko.
|
||||
|
||||
**2. Certainty Equivalent (CE):** CE(L) = pewna kwota równoważna loterii L.
|
||||
- CE < E[X] → risk averse (wklęsła U)
|
||||
- CE = E[X] → risk neutral
|
||||
- CE > E[X] → risk seeking
|
||||
- Risk Premium = E[X] − CE
|
||||
**1. Kryterium Laplace'a:** Załóż równe prawdopodobieństwa stanów (warunki NIEPEWNOŚCI). Oblicz średnią wypłat per alternatywa. Wybierz max średniej. Formuła: V(Aᵢ) = (1/n) × Σⱼ aᵢⱼ.
|
||||
|
||||
**3. AHP (Analytic Hierarchy Process):** Hierarchia: Cel → Kryteria → Alternatywy. Porównania parami (skala 1-9) → eigenvalue → wagi. Consistency Ratio CR < 0.1.
|
||||
**2. Kryterium optymistyczne (maximax):** Dla każdej alternatywy weź max wypłatę. Wybierz alternatywę z max z tych max. Formuła: max maxⱼ aᵢⱼ.
|
||||
|
||||
**4. PROMETHEE:** Funkcje preferencji per kryterium; agregacja; przepływy Φ⁺, Φ⁻, Φ (net); ranking.
|
||||
**3. Kryterium pesymistyczne (maximin / Walda):** Dla każdej alternatywy weź min wypłatę. Wybierz alternatywę z max z tych min. Formuła: max minⱼ aᵢⱼ.
|
||||
|
||||
**5. ELECTRE:** Concordance (zgoda) + Discordance (sprzeciw) → outranking aSb.
|
||||
**4. Kryterium Hurwicza:** Kompromis: V(Aᵢ) = α × maxⱼ aᵢⱼ + (1−α) × minⱼ aᵢⱼ. Decydent podaje α ∈ [0,1]. α=1 → maximax, α=0 → maximin.
|
||||
|
||||
**5. Kryterium Savage'a (minimax regret):** Zbuduj macierz żalu (rᵢⱼ = maxₖ aₖⱼ − aᵢⱼ). Dla każdej alternatywy weź max żal. Wybierz alternatywę z min max żalu.
|
||||
|
||||
### Etymologia
|
||||
|
||||
**AHP** — Thomas Saaty (U. of Pittsburgh, 1970s); Analytic Hierarchy Process. **PROMETHEE** — Preference Ranking Organization METHod for Enrichment Evaluations (Jean-Pierre Brans, 1982). **ELECTRE** — ÉLimination Et Choix Traduisant la REalité (Bernard Roy, 1965) = „Eliminacja i Wybór Odzwierciedlający Rzeczywistość". **Certainty Equivalent** — z teorii użyteczności von Neumanna-Morgensterna (1944). **Funkcja użyteczności** — Daniel Bernoulli (1738) wprowadził koncepcję; vN-M sformalizowali aksjomatycznie.
|
||||
**Wartość oczekiwana** — pojęcie z XVII w., Blaise Pascal i Pierre de Fermat (1654), formalizacja hazardu; „ile przeciętnie wygrasz?". **Laplace** — Pierre-Simon de Laplace (1749–1827), francuski matematyk; zasada niedostatecznej racji (principle of insufficient reason) — jeśli nie mamy powodu faworyzować żadnego stanu, traktujemy je jako równie prawdopodobne. **Wald** — Abraham Wald (1902–1950), matematyk z Wiednia; kryterium maximin = strategia minimax z teorii gier. **Hurwicz** — Leonid Hurwicz (1917–2008), laureat Nobla z ekonomii 2007 (z Myersonem i Maskinem, za mechanism design); zaproponował kompromis z parametrem α. **Savage** — Leonard Jimmie Savage (1917–1971), amerykański statystyk; kryterium minimax regret — minimalizacja żalu (1951, „The Foundations of Statistics").
|
||||
|
||||
### Jak zapamiętać
|
||||
|
||||
- **CE = „ile dałbyś za pewniaka zamiast loterii?"** → miara awersji do ryzyka
|
||||
- **AHP = „porównaj parami, policz wagi"** (macierz → eigenvalue)
|
||||
- **PROMETHEE = „przepływy"** (Φ⁺ outgoing, Φ⁻ incoming)
|
||||
- **E[X] = „średnia ważona prawdopodobieństwami"** → jak średnia ocen w dzienniku, ale wagi to szanse
|
||||
- **Laplace = „wszystko po równo"** → średnia arytmetyczna wypłat (Loteria — ALE Po równo)
|
||||
- **Maximax = „marzyciel → max z max"** → najlepszy z najlepszych, ignoruje ryzyko
|
||||
- **Maximin = „mur obronny → max z min"** → najlepszy z najgorszych (Wald = Wall = Mur)
|
||||
- **Hurwicz = „huśtawka — α pomiędzy"** → α·max + (1−α)·min, kręcisz pokrętłem optymizmu
|
||||
- **Savage = „szał żalu → min max żalu"** → macierz żalu → minimalizuj maksymalny żal (trzymaj żal na smyczy)
|
||||
|
||||
|
||||