2626"""
2727
2828import doctest
29-
3029import numpy as np
3130from sklearn .datasets import load_iris
3231
@@ -38,49 +37,57 @@ def collect_dataset() -> tuple[np.ndarray, np.ndarray]:
3837 :return: Tuple containing feature matrix and target labels
3938
4039 Example:
41- >>> x, y = collect_dataset()
42- >>> x .shape
40+ >>> data_x, data_y = collect_dataset()
41+ >>> data_x .shape
4342 (150, 4)
44- >>> y .shape
43+ >>> data_y .shape
4544 (150,)
4645 """
4746 data = load_iris ()
4847 return np .array (data .data ), np .array (data .target )
4948
5049
51- def compute_pairwise_affinities (x : np .ndarray , sigma : float = 1.0 ) -> np .ndarray :
50+ def compute_pairwise_affinities (data_x : np .ndarray , sigma : float = 1.0 ) -> np .ndarray :
5251 """
5352 Computes pairwise affinities (P matrix) in high-dimensional space using Gaussian kernel.
5453
55- :param x : Input data of shape (n_samples, n_features)
54+ :param data_x : Input data of shape (n_samples, n_features)
5655 :param sigma: Variance (Bandwidth) of the Gaussian kernel
5756 :return: Symmetrized probability matrix p
5857
5958 Example:
6059 >>> import numpy as np
61- >>> x = np.array([[0.0, 0.0], [1.0, 0.0]])
62- >>> p = compute_pairwise_affinities(x )
60+ >>> data_x = np.array([[0.0, 0.0], [1.0, 0.0]])
61+ >>> p = compute_pairwise_affinities(data_x )
6362 >>> float(round(p[0, 1], 3))
6463 0.25
6564 """
66- n_samples = x .shape [0 ]
67- sum_x = np .sum (np .square (x ), axis = 1 )
68- d = np .add (np .add (- 2 * np .dot (x , x .T ), sum_x ).T , sum_x )
65+ n_samples = data_x .shape [0 ]
66+ sum_x = np .sum (np .square (data_x ), axis = 1 )
67+ d = np .add (np .add (- 2 * np .dot (data_x , data_x .T ), sum_x ).T , sum_x )
6968 p = np .exp (- d / (2 * sigma ** 2 ))
7069 np .fill_diagonal (p , 0 )
7170 p /= np .sum (p )
7271 return (p + p .T ) / (2 * n_samples )
7372
7473
75- def compute_low_dim_affinities (y : np .ndarray ) -> tuple [np .ndarray , np .ndarray ]:
74+ def compute_low_dim_affinities (embedding_y : np .ndarray ) -> tuple [np .ndarray , np .ndarray ]:
7675 """
7776 Computes low-dimensional similarities (Q matrix) using Student-t distribution.
7877
79- :param y : Low-dimensional embeddings (n_samples, n_components)
78+ :param embedding_y : Low-dimensional embeddings (n_samples, n_components)
8079 :return: Tuple (q, num) where q is the probability matrix and num is numerator array
80+
81+ Example:
82+ >>> embedding_y = np.array([[0.0, 0.0], [1.0, 0.0]])
83+ >>> q, num = compute_low_dim_affinities(embedding_y)
84+ >>> q.shape
85+ (2, 2)
86+ >>> num.shape
87+ (2, 2)
8188 """
82- sum_y = np .sum (np .square (y ), axis = 1 )
83- num = 1 / (1 + np .add (np .add (- 2 * np .dot (y , y .T ), sum_y ).T , sum_y ))
89+ sum_y = np .sum (np .square (embedding_y ), axis = 1 )
90+ num = 1 / (1 + np .add (np .add (- 2 * np .dot (embedding_y , embedding_y .T ), sum_y ).T , sum_y ))
8491 np .fill_diagonal (num , 0 )
8592 q = num / np .sum (num )
8693 return q , num
@@ -102,8 +109,8 @@ def apply_tsne(
102109 :return: Transformed dataset (low-dimensional embedding)
103110
104111 Example:
105- >>> x , _ = collect_dataset()
106- >>> y_emb = apply_tsne(x , n_components=2, n_iter=50)
112+ >>> data_x , _ = collect_dataset()
113+ >>> y_emb = apply_tsne(data_x , n_components=2, n_iter=50)
107114 >>> y_emb.shape
108115 (150, 2)
109116 """
@@ -115,49 +122,54 @@ def apply_tsne(
115122 n_samples = data_x .shape [0 ]
116123
117124 # Initialize low-dimensional map randomly
118- y = np .random .randn (n_samples , n_components ) * 1e-4
125+ y_emb = np .random .randn (n_samples , n_components ) * 1e-4
119126 p = compute_pairwise_affinities (data_x )
120127 p = np .maximum (p , 1e-12 )
121128
122129 # Initialize parameters
123- y_inc = np .zeros_like (y )
130+ y_inc = np .zeros_like (y_emb )
124131 momentum = 0.5
125132
126133 for i in range (n_iter ):
127- q , num = compute_low_dim_affinities (y )
134+ q , num = compute_low_dim_affinities (y_emb )
128135 q = np .maximum (q , 1e-12 )
129136
130137 pq = p - q
131138
132139 # Compute gradient
133140 d_y = 4 * (
134- np .dot ((pq * num ), y )
135- - np .multiply (np .sum (pq * num , axis = 1 )[:, np .newaxis ], y )
141+ np .dot ((pq * num ), y_emb )
142+ - np .multiply (np .sum (pq * num , axis = 1 )[:, np .newaxis ], y_emb )
136143 )
137144
138145 # Update with momentum and learning rate
139146 y_inc = momentum * y_inc - learning_rate * d_y
140- y += y_inc
147+ y_emb += y_inc
141148
142149 # Adjust momentum halfway through
143150 if i == int (n_iter / 4 ):
144151 momentum = 0.8
145152
146- return y
153+ return y_emb
147154
148155
149156def main () -> None :
150157 """
151158 Driver function for t-SNE demonstration.
159+
160+ Example:
161+ >>> main() # doctest: +ELLIPSIS
162+ t-SNE embedding (first 5 points):
163+ ...
152164 """
153- x , y_labels = collect_dataset ()
154- y_emb = apply_tsne (x , n_components = 2 , n_iter = 300 )
165+ data_x , data_y = collect_dataset ()
166+ y_emb = apply_tsne (data_x , n_components = 2 , n_iter = 300 )
155167 print ("t-SNE embedding (first 5 points):" )
156168 print (y_emb [:5 ])
157169
158170 # Optional visualization (commented to avoid dependency)
159171 # import matplotlib.pyplot as plt
160- # plt.scatter(y_emb[:, 0], y_emb[:, 1], c=y_labels , cmap="viridis")
172+ # plt.scatter(y_emb[:, 0], y_emb[:, 1], c=data_y , cmap="viridis")
161173 # plt.title("t-SNE Visualization of Iris Dataset")
162174 # plt.xlabel("Component 1")
163175 # plt.ylabel("Component 2")
@@ -181,13 +193,13 @@ def main() -> None:
181193- n_iter: number of iterations for optimization
182194
183195Output:
184- - y : numpy array of shape (n_samples, n_components)
196+ - y_emb : numpy array of shape (n_samples, n_components)
185197 Each row is the low-dimensional embedding of the corresponding high-dimensional point.
186198
187199How it works:
188- 1. Compute high-dimensional similarities (p matrix)
189- 2. Initialize low-dimensional map (y ) randomly
190- 3. Compute low-dimensional similarities (q matrix)
191- 4. Minimize KL divergence between p and q using gradient descent
192- 5. Update y with momentum and learning rate iteratively
200+ 1. Compute high-dimensional similarities (P matrix)
201+ 2. Initialize low-dimensional map (y_emb ) randomly
202+ 3. Compute low-dimensional similarities (Q matrix)
203+ 4. Minimize KL divergence between P and Q using gradient descent
204+ 5. Update y_emb with momentum and learning rate iteratively
193205"""
0 commit comments