ilovetools 0.2.26__tar.gz → 0.2.28__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. {ilovetools-0.2.26/ilovetools.egg-info → ilovetools-0.2.28}/PKG-INFO +3 -2
  2. ilovetools-0.2.28/ilovetools/ml/augmentation.py +624 -0
  3. ilovetools-0.2.28/ilovetools/ml/losses.py +758 -0
  4. {ilovetools-0.2.26 → ilovetools-0.2.28/ilovetools.egg-info}/PKG-INFO +3 -2
  5. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools.egg-info/SOURCES.txt +4 -0
  6. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools.egg-info/requires.txt +1 -0
  7. {ilovetools-0.2.26 → ilovetools-0.2.28}/pyproject.toml +3 -2
  8. {ilovetools-0.2.26 → ilovetools-0.2.28}/setup.py +3 -2
  9. ilovetools-0.2.28/tests/test_augmentation.py +485 -0
  10. ilovetools-0.2.28/tests/test_losses.py +540 -0
  11. {ilovetools-0.2.26 → ilovetools-0.2.28}/LICENSE +0 -0
  12. {ilovetools-0.2.26 → ilovetools-0.2.28}/MANIFEST.in +0 -0
  13. {ilovetools-0.2.26 → ilovetools-0.2.28}/README.md +0 -0
  14. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/__init__.py +0 -0
  15. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/ai/__init__.py +0 -0
  16. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/ai/embeddings.py +0 -0
  17. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/ai/inference.py +0 -0
  18. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/ai/llm_helpers.py +0 -0
  19. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/audio/__init__.py +0 -0
  20. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/automation/__init__.py +0 -0
  21. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/automation/file_organizer.py +0 -0
  22. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/conversion/__init__.py +0 -0
  23. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/conversion/config_converter.py +0 -0
  24. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/conversion/config_converter_fixed_header.py +0 -0
  25. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/data/__init__.py +0 -0
  26. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/data/feature_engineering.py +0 -0
  27. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/data/preprocessing.py +0 -0
  28. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/database/__init__.py +0 -0
  29. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/datetime/__init__.py +0 -0
  30. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/email/__init__.py +0 -0
  31. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/email/template_engine.py +0 -0
  32. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/files/__init__.py +0 -0
  33. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/image/__init__.py +0 -0
  34. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/ml/__init__.py +0 -0
  35. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/ml/activations.py +0 -0
  36. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/ml/anomaly_detection.py +0 -0
  37. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/ml/attention.py +0 -0
  38. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/ml/clustering.py +0 -0
  39. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/ml/cnn.py +0 -0
  40. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/ml/cross_validation.py +0 -0
  41. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/ml/dimensionality.py +0 -0
  42. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/ml/dropout.py +0 -0
  43. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/ml/ensemble.py +0 -0
  44. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/ml/feature_selection.py +0 -0
  45. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/ml/gradient_descent.py +0 -0
  46. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/ml/imbalanced.py +0 -0
  47. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/ml/interpretation.py +0 -0
  48. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/ml/loss_functions.py +0 -0
  49. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/ml/lr_schedulers.py +0 -0
  50. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/ml/metrics.py +0 -0
  51. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/ml/neural_network.py +0 -0
  52. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/ml/normalization.py +0 -0
  53. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/ml/normalization_advanced.py +0 -0
  54. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/ml/optimizers.py +0 -0
  55. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/ml/pipeline.py +0 -0
  56. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/ml/positional_encoding.py +0 -0
  57. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/ml/regularization.py +0 -0
  58. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/ml/rnn.py +0 -0
  59. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/ml/timeseries.py +0 -0
  60. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/ml/tuning.py +0 -0
  61. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/ml/weight_init.py +0 -0
  62. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/security/__init__.py +0 -0
  63. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/security/password_checker.py +0 -0
  64. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/text/__init__.py +0 -0
  65. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/utils/__init__.py +0 -0
  66. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/utils/cache_system.py +0 -0
  67. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/utils/logger.py +0 -0
  68. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/utils/rate_limiter.py +0 -0
  69. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/utils/retry.py +0 -0
  70. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/validation/__init__.py +0 -0
  71. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/validation/data_validator.py +0 -0
  72. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/web/__init__.py +0 -0
  73. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/web/scraper.py +0 -0
  74. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools/web/url_shortener.py +0 -0
  75. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools.egg-info/dependency_links.txt +0 -0
  76. {ilovetools-0.2.26 → ilovetools-0.2.28}/ilovetools.egg-info/top_level.txt +0 -0
  77. {ilovetools-0.2.26 → ilovetools-0.2.28}/requirements.txt +0 -0
  78. {ilovetools-0.2.26 → ilovetools-0.2.28}/setup.cfg +0 -0
  79. {ilovetools-0.2.26 → ilovetools-0.2.28}/tests/__init__.py +0 -0
  80. {ilovetools-0.2.26 → ilovetools-0.2.28}/tests/test_activations.py +0 -0
  81. {ilovetools-0.2.26 → ilovetools-0.2.28}/tests/test_attention.py +0 -0
  82. {ilovetools-0.2.26 → ilovetools-0.2.28}/tests/test_cnn.py +0 -0
  83. {ilovetools-0.2.26 → ilovetools-0.2.28}/tests/test_dropout.py +0 -0
  84. {ilovetools-0.2.26 → ilovetools-0.2.28}/tests/test_gradient_descent.py +0 -0
  85. {ilovetools-0.2.26 → ilovetools-0.2.28}/tests/test_loss_functions.py +0 -0
  86. {ilovetools-0.2.26 → ilovetools-0.2.28}/tests/test_lr_schedulers.py +0 -0
  87. {ilovetools-0.2.26 → ilovetools-0.2.28}/tests/test_neural_network.py +0 -0
  88. {ilovetools-0.2.26 → ilovetools-0.2.28}/tests/test_normalization.py +0 -0
  89. {ilovetools-0.2.26 → ilovetools-0.2.28}/tests/test_normalization_advanced.py +0 -0
  90. {ilovetools-0.2.26 → ilovetools-0.2.28}/tests/test_optimizers.py +0 -0
  91. {ilovetools-0.2.26 → ilovetools-0.2.28}/tests/test_positional_encoding.py +0 -0
  92. {ilovetools-0.2.26 → ilovetools-0.2.28}/tests/test_pypi_installation.py +0 -0
  93. {ilovetools-0.2.26 → ilovetools-0.2.28}/tests/test_regularization.py +0 -0
  94. {ilovetools-0.2.26 → ilovetools-0.2.28}/tests/test_rnn.py +0 -0
  95. {ilovetools-0.2.26 → ilovetools-0.2.28}/tests/test_weight_init.py +0 -0
  96. {ilovetools-0.2.26 → ilovetools-0.2.28}/tests/verify_positional_encoding.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ilovetools
3
- Version: 0.2.26
3
+ Version: 0.2.28
4
4
  Summary: A comprehensive Python utility library with modular tools for AI/ML, data processing, and daily programming needs
5
5
  Home-page: https://github.com/AliMehdi512/ilovetools
6
6
  Author: Ali Mehdi
@@ -11,7 +11,7 @@ Project-URL: Repository, https://github.com/AliMehdi512/ilovetools
11
11
  Project-URL: Issues, https://github.com/AliMehdi512/ilovetools/issues
12
12
  Project-URL: Bug Reports, https://github.com/AliMehdi512/ilovetools/issues
13
13
  Project-URL: Source, https://github.com/AliMehdi512/ilovetools
14
- Keywords: utilities,tools,ai,ml,data-processing,automation,dropout,regularization,overfitting,spatial-dropout,variational-dropout,dropconnect,alpha-dropout,deep-learning,neural-networks
14
+ Keywords: utilities,tools,ai,ml,data-processing,automation,data-augmentation,image-augmentation,text-augmentation,mixup,cutout,random-erasing,autoaugment,randaugment,albumentations,computer-vision,nlp,deep-learning,neural-networks,training,generalization,overfitting
15
15
  Classifier: Development Status :: 3 - Alpha
16
16
  Classifier: Intended Audience :: Developers
17
17
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
@@ -27,6 +27,7 @@ License-File: LICENSE
27
27
  Requires-Dist: requests>=2.31.0
28
28
  Requires-Dist: numpy>=1.24.0
29
29
  Requires-Dist: pandas>=2.0.0
30
+ Requires-Dist: scipy>=1.10.0
30
31
  Provides-Extra: ai
31
32
  Requires-Dist: openai>=1.0.0; extra == "ai"
32
33
  Requires-Dist: transformers>=4.30.0; extra == "ai"
@@ -0,0 +1,624 @@
1
+ """
2
+ Data Augmentation Suite
3
+
4
+ This module implements various data augmentation techniques for training neural networks.
5
+ Augmentation artificially increases dataset size and diversity, improving generalization.
6
+
7
+ Implemented Techniques:
8
+
9
+ Image Augmentation:
10
+ 1. Rotation - Rotate images by random angles
11
+ 2. Horizontal Flip - Mirror images horizontally
12
+ 3. Vertical Flip - Mirror images vertically
13
+ 4. Random Crop - Extract random patches
14
+ 5. Center Crop - Extract center region
15
+ 6. Color Jitter - Adjust brightness, contrast, saturation
16
+ 7. Gaussian Noise - Add random noise
17
+ 8. Gaussian Blur - Apply blur filter
18
+ 9. Random Erasing - Erase random rectangular regions
19
+ 10. Cutout - Mask out square regions
20
+ 11. Mixup - Blend two images
21
+ 12. Normalize - Standardize pixel values
22
+
23
+ Text Augmentation:
24
+ 1. Synonym Replacement - Replace words with synonyms
25
+ 2. Random Insertion - Insert random words
26
+ 3. Random Swap - Swap word positions
27
+ 4. Random Deletion - Delete random words
28
+
29
+ References:
30
+ - Rotation/Flip/Crop: Classic augmentation techniques
31
+ - Mixup: "mixup: Beyond Empirical Risk Minimization" (Zhang et al., 2017)
32
+ - Cutout: "Improved Regularization of CNNs with Cutout" (DeVries & Taylor, 2017)
33
+ - Random Erasing: "Random Erasing Data Augmentation" (Zhong et al., 2017)
34
+ - AutoAugment: "AutoAugment: Learning Augmentation Policies" (Cubuk et al., 2018)
35
+
36
+ Author: Ali Mehdi
37
+ Date: January 15, 2026
38
+ """
39
+
40
+ import numpy as np
41
+ from typing import Tuple, Optional, List
42
+ import random
43
+
44
+
45
+ class ImageAugmenter:
46
+ """
47
+ Image Augmentation Pipeline.
48
+
49
+ Provides various augmentation techniques for image data.
50
+ All methods work with NumPy arrays in (H, W, C) format.
51
+
52
+ Example:
53
+ >>> aug = ImageAugmenter()
54
+ >>> image = np.random.rand(224, 224, 3)
55
+ >>> rotated = aug.rotate(image, angle=30)
56
+ >>> flipped = aug.horizontal_flip(image)
57
+ >>> noisy = aug.gaussian_noise(image, std=0.1)
58
+ """
59
+
60
+ def __init__(self, seed: Optional[int] = None):
61
+ """
62
+ Initialize augmenter.
63
+
64
+ Args:
65
+ seed: Random seed for reproducibility
66
+ """
67
+ if seed is not None:
68
+ np.random.seed(seed)
69
+ random.seed(seed)
70
+
71
+ def rotate(self, image: np.ndarray, angle: float) -> np.ndarray:
72
+ """
73
+ Rotate image by specified angle.
74
+
75
+ Args:
76
+ image: Input image (H, W, C)
77
+ angle: Rotation angle in degrees (positive = counter-clockwise)
78
+
79
+ Returns:
80
+ Rotated image
81
+
82
+ Example:
83
+ >>> aug = ImageAugmenter()
84
+ >>> image = np.random.rand(100, 100, 3)
85
+ >>> rotated = aug.rotate(image, angle=45)
86
+ """
87
+ from scipy.ndimage import rotate as scipy_rotate
88
+ return scipy_rotate(image, angle, reshape=False, mode='nearest')
89
+
90
+ def random_rotation(self, image: np.ndarray, max_angle: float = 30) -> np.ndarray:
91
+ """
92
+ Rotate image by random angle.
93
+
94
+ Args:
95
+ image: Input image (H, W, C)
96
+ max_angle: Maximum rotation angle in degrees
97
+
98
+ Returns:
99
+ Randomly rotated image
100
+ """
101
+ angle = np.random.uniform(-max_angle, max_angle)
102
+ return self.rotate(image, angle)
103
+
104
+ def horizontal_flip(self, image: np.ndarray) -> np.ndarray:
105
+ """
106
+ Flip image horizontally (left-right).
107
+
108
+ Args:
109
+ image: Input image (H, W, C)
110
+
111
+ Returns:
112
+ Horizontally flipped image
113
+
114
+ Example:
115
+ >>> aug = ImageAugmenter()
116
+ >>> image = np.random.rand(100, 100, 3)
117
+ >>> flipped = aug.horizontal_flip(image)
118
+ """
119
+ return np.fliplr(image)
120
+
121
+ def vertical_flip(self, image: np.ndarray) -> np.ndarray:
122
+ """
123
+ Flip image vertically (up-down).
124
+
125
+ Args:
126
+ image: Input image (H, W, C)
127
+
128
+ Returns:
129
+ Vertically flipped image
130
+
131
+ Example:
132
+ >>> aug = ImageAugmenter()
133
+ >>> image = np.random.rand(100, 100, 3)
134
+ >>> flipped = aug.vertical_flip(image)
135
+ """
136
+ return np.flipud(image)
137
+
138
+ def random_crop(self, image: np.ndarray, crop_size: Tuple[int, int]) -> np.ndarray:
139
+ """
140
+ Extract random crop from image.
141
+
142
+ Args:
143
+ image: Input image (H, W, C)
144
+ crop_size: (height, width) of crop
145
+
146
+ Returns:
147
+ Randomly cropped image
148
+
149
+ Example:
150
+ >>> aug = ImageAugmenter()
151
+ >>> image = np.random.rand(224, 224, 3)
152
+ >>> cropped = aug.random_crop(image, crop_size=(128, 128))
153
+ """
154
+ h, w = image.shape[:2]
155
+ crop_h, crop_w = crop_size
156
+
157
+ if crop_h > h or crop_w > w:
158
+ raise ValueError(f"Crop size {crop_size} larger than image {(h, w)}")
159
+
160
+ top = np.random.randint(0, h - crop_h + 1)
161
+ left = np.random.randint(0, w - crop_w + 1)
162
+
163
+ return image[top:top + crop_h, left:left + crop_w]
164
+
165
+ def center_crop(self, image: np.ndarray, crop_size: Tuple[int, int]) -> np.ndarray:
166
+ """
167
+ Extract center crop from image.
168
+
169
+ Args:
170
+ image: Input image (H, W, C)
171
+ crop_size: (height, width) of crop
172
+
173
+ Returns:
174
+ Center cropped image
175
+
176
+ Example:
177
+ >>> aug = ImageAugmenter()
178
+ >>> image = np.random.rand(224, 224, 3)
179
+ >>> cropped = aug.center_crop(image, crop_size=(128, 128))
180
+ """
181
+ h, w = image.shape[:2]
182
+ crop_h, crop_w = crop_size
183
+
184
+ if crop_h > h or crop_w > w:
185
+ raise ValueError(f"Crop size {crop_size} larger than image {(h, w)}")
186
+
187
+ top = (h - crop_h) // 2
188
+ left = (w - crop_w) // 2
189
+
190
+ return image[top:top + crop_h, left:left + crop_w]
191
+
192
+ def color_jitter(self, image: np.ndarray, brightness: float = 0.2,
193
+ contrast: float = 0.2, saturation: float = 0.2) -> np.ndarray:
194
+ """
195
+ Randomly adjust brightness, contrast, and saturation.
196
+
197
+ Args:
198
+ image: Input image (H, W, C) in range [0, 1]
199
+ brightness: Max brightness adjustment factor
200
+ contrast: Max contrast adjustment factor
201
+ saturation: Max saturation adjustment factor
202
+
203
+ Returns:
204
+ Color-jittered image
205
+
206
+ Example:
207
+ >>> aug = ImageAugmenter()
208
+ >>> image = np.random.rand(100, 100, 3)
209
+ >>> jittered = aug.color_jitter(image, brightness=0.3)
210
+ """
211
+ # Brightness
212
+ brightness_factor = 1 + np.random.uniform(-brightness, brightness)
213
+ image = np.clip(image * brightness_factor, 0, 1)
214
+
215
+ # Contrast
216
+ contrast_factor = 1 + np.random.uniform(-contrast, contrast)
217
+ mean = image.mean(axis=(0, 1), keepdims=True)
218
+ image = np.clip((image - mean) * contrast_factor + mean, 0, 1)
219
+
220
+ # Saturation (convert to HSV, adjust S channel)
221
+ if saturation > 0 and image.shape[-1] == 3:
222
+ saturation_factor = 1 + np.random.uniform(-saturation, saturation)
223
+ # Simple saturation adjustment in RGB space
224
+ gray = image.mean(axis=-1, keepdims=True)
225
+ image = np.clip((image - gray) * saturation_factor + gray, 0, 1)
226
+
227
+ return image
228
+
229
+ def gaussian_noise(self, image: np.ndarray, mean: float = 0.0,
230
+ std: float = 0.1) -> np.ndarray:
231
+ """
232
+ Add Gaussian noise to image.
233
+
234
+ Args:
235
+ image: Input image (H, W, C)
236
+ mean: Mean of Gaussian noise
237
+ std: Standard deviation of Gaussian noise
238
+
239
+ Returns:
240
+ Noisy image
241
+
242
+ Example:
243
+ >>> aug = ImageAugmenter()
244
+ >>> image = np.random.rand(100, 100, 3)
245
+ >>> noisy = aug.gaussian_noise(image, std=0.05)
246
+ """
247
+ noise = np.random.normal(mean, std, image.shape)
248
+ noisy_image = image + noise
249
+ return np.clip(noisy_image, 0, 1)
250
+
251
+ def gaussian_blur(self, image: np.ndarray, sigma: float = 1.0) -> np.ndarray:
252
+ """
253
+ Apply Gaussian blur to image.
254
+
255
+ Args:
256
+ image: Input image (H, W, C)
257
+ sigma: Standard deviation of Gaussian kernel
258
+
259
+ Returns:
260
+ Blurred image
261
+
262
+ Example:
263
+ >>> aug = ImageAugmenter()
264
+ >>> image = np.random.rand(100, 100, 3)
265
+ >>> blurred = aug.gaussian_blur(image, sigma=2.0)
266
+ """
267
+ from scipy.ndimage import gaussian_filter
268
+ return gaussian_filter(image, sigma=(sigma, sigma, 0))
269
+
270
+ def random_erasing(self, image: np.ndarray, probability: float = 0.5,
271
+ area_ratio_range: Tuple[float, float] = (0.02, 0.4),
272
+ aspect_ratio_range: Tuple[float, float] = (0.3, 3.3)) -> np.ndarray:
273
+ """
274
+ Randomly erase rectangular regions (Random Erasing augmentation).
275
+
276
+ Args:
277
+ image: Input image (H, W, C)
278
+ probability: Probability of applying erasing
279
+ area_ratio_range: Range of erased area ratio
280
+ aspect_ratio_range: Range of aspect ratio
281
+
282
+ Returns:
283
+ Image with random erasing applied
284
+
285
+ Example:
286
+ >>> aug = ImageAugmenter()
287
+ >>> image = np.random.rand(100, 100, 3)
288
+ >>> erased = aug.random_erasing(image, probability=0.5)
289
+
290
+ Reference:
291
+ Zhong et al., "Random Erasing Data Augmentation", 2017
292
+ """
293
+ if np.random.random() > probability:
294
+ return image
295
+
296
+ h, w = image.shape[:2]
297
+ area = h * w
298
+
299
+ for _ in range(100): # Try up to 100 times
300
+ target_area = np.random.uniform(*area_ratio_range) * area
301
+ aspect_ratio = np.random.uniform(*aspect_ratio_range)
302
+
303
+ erase_h = int(np.sqrt(target_area * aspect_ratio))
304
+ erase_w = int(np.sqrt(target_area / aspect_ratio))
305
+
306
+ if erase_h < h and erase_w < w:
307
+ top = np.random.randint(0, h - erase_h)
308
+ left = np.random.randint(0, w - erase_w)
309
+
310
+ # Erase with random values
311
+ image = image.copy()
312
+ image[top:top + erase_h, left:left + erase_w] = np.random.random(
313
+ (erase_h, erase_w, image.shape[2])
314
+ )
315
+ break
316
+
317
+ return image
318
+
319
+ def cutout(self, image: np.ndarray, n_holes: int = 1,
320
+ length: int = 16) -> np.ndarray:
321
+ """
322
+ Apply Cutout augmentation (mask out square regions).
323
+
324
+ Args:
325
+ image: Input image (H, W, C)
326
+ n_holes: Number of holes to cut out
327
+ length: Side length of square holes
328
+
329
+ Returns:
330
+ Image with cutout applied
331
+
332
+ Example:
333
+ >>> aug = ImageAugmenter()
334
+ >>> image = np.random.rand(100, 100, 3)
335
+ >>> cutout_img = aug.cutout(image, n_holes=1, length=20)
336
+
337
+ Reference:
338
+ DeVries & Taylor, "Improved Regularization of CNNs with Cutout", 2017
339
+ """
340
+ h, w = image.shape[:2]
341
+ image = image.copy()
342
+
343
+ for _ in range(n_holes):
344
+ y = np.random.randint(h)
345
+ x = np.random.randint(w)
346
+
347
+ y1 = np.clip(y - length // 2, 0, h)
348
+ y2 = np.clip(y + length // 2, 0, h)
349
+ x1 = np.clip(x - length // 2, 0, w)
350
+ x2 = np.clip(x + length // 2, 0, w)
351
+
352
+ image[y1:y2, x1:x2] = 0
353
+
354
+ return image
355
+
356
+ def mixup(self, image1: np.ndarray, image2: np.ndarray,
357
+ alpha: float = 0.2) -> Tuple[np.ndarray, float]:
358
+ """
359
+ Apply Mixup augmentation (blend two images).
360
+
361
+ Args:
362
+ image1: First input image (H, W, C)
363
+ image2: Second input image (H, W, C)
364
+ alpha: Beta distribution parameter
365
+
366
+ Returns:
367
+ Tuple of (mixed image, mixing coefficient lambda)
368
+
369
+ Example:
370
+ >>> aug = ImageAugmenter()
371
+ >>> img1 = np.random.rand(100, 100, 3)
372
+ >>> img2 = np.random.rand(100, 100, 3)
373
+ >>> mixed, lam = aug.mixup(img1, img2, alpha=0.2)
374
+
375
+ Reference:
376
+ Zhang et al., "mixup: Beyond Empirical Risk Minimization", 2017
377
+ """
378
+ if image1.shape != image2.shape:
379
+ raise ValueError(f"Image shapes must match: {image1.shape} vs {image2.shape}")
380
+
381
+ lam = np.random.beta(alpha, alpha) if alpha > 0 else 1
382
+ mixed_image = lam * image1 + (1 - lam) * image2
383
+
384
+ return mixed_image, lam
385
+
386
+ def normalize(self, image: np.ndarray, mean: Tuple[float, ...],
387
+ std: Tuple[float, ...]) -> np.ndarray:
388
+ """
389
+ Normalize image with mean and standard deviation.
390
+
391
+ Args:
392
+ image: Input image (H, W, C)
393
+ mean: Mean for each channel
394
+ std: Standard deviation for each channel
395
+
396
+ Returns:
397
+ Normalized image
398
+
399
+ Example:
400
+ >>> aug = ImageAugmenter()
401
+ >>> image = np.random.rand(100, 100, 3)
402
+ >>> normalized = aug.normalize(image, mean=(0.5, 0.5, 0.5), std=(0.5, 0.5, 0.5))
403
+ """
404
+ mean = np.array(mean).reshape(1, 1, -1)
405
+ std = np.array(std).reshape(1, 1, -1)
406
+ return (image - mean) / std
407
+
408
+
409
+ class TextAugmenter:
410
+ """
411
+ Text Augmentation Pipeline.
412
+
413
+ Provides various augmentation techniques for text data.
414
+ Useful for NLP tasks with limited training data.
415
+
416
+ Example:
417
+ >>> aug = TextAugmenter()
418
+ >>> text = "The quick brown fox jumps over the lazy dog"
419
+ >>> augmented = aug.random_deletion(text, p=0.1)
420
+ """
421
+
422
+ def __init__(self, seed: Optional[int] = None):
423
+ """
424
+ Initialize text augmenter.
425
+
426
+ Args:
427
+ seed: Random seed for reproducibility
428
+ """
429
+ if seed is not None:
430
+ random.seed(seed)
431
+
432
+ def synonym_replacement(self, text: str, n: int = 1) -> str:
433
+ """
434
+ Replace n words with synonyms.
435
+
436
+ Note: This is a simplified version. For production, use libraries
437
+ like NLTK with WordNet for actual synonym replacement.
438
+
439
+ Args:
440
+ text: Input text
441
+ n: Number of words to replace
442
+
443
+ Returns:
444
+ Text with synonyms replaced
445
+
446
+ Example:
447
+ >>> aug = TextAugmenter()
448
+ >>> text = "The quick brown fox"
449
+ >>> augmented = aug.synonym_replacement(text, n=1)
450
+ """
451
+ words = text.split()
452
+
453
+ # Simple synonym dictionary (expand for production use)
454
+ synonyms = {
455
+ 'quick': ['fast', 'rapid', 'swift'],
456
+ 'brown': ['tan', 'beige', 'chocolate'],
457
+ 'lazy': ['idle', 'sluggish', 'inactive'],
458
+ 'big': ['large', 'huge', 'enormous'],
459
+ 'small': ['tiny', 'little', 'mini'],
460
+ }
461
+
462
+ for _ in range(n):
463
+ replaceable = [i for i, w in enumerate(words) if w.lower() in synonyms]
464
+ if not replaceable:
465
+ break
466
+
467
+ idx = random.choice(replaceable)
468
+ word = words[idx].lower()
469
+ words[idx] = random.choice(synonyms[word])
470
+
471
+ return ' '.join(words)
472
+
473
+ def random_insertion(self, text: str, n: int = 1) -> str:
474
+ """
475
+ Randomly insert n words into the text.
476
+
477
+ Args:
478
+ text: Input text
479
+ n: Number of words to insert
480
+
481
+ Returns:
482
+ Text with random insertions
483
+
484
+ Example:
485
+ >>> aug = TextAugmenter()
486
+ >>> text = "The quick fox"
487
+ >>> augmented = aug.random_insertion(text, n=1)
488
+ """
489
+ words = text.split()
490
+
491
+ for _ in range(n):
492
+ if not words:
493
+ break
494
+
495
+ # Insert a random word from the text
496
+ random_word = random.choice(words)
497
+ random_idx = random.randint(0, len(words))
498
+ words.insert(random_idx, random_word)
499
+
500
+ return ' '.join(words)
501
+
502
+ def random_swap(self, text: str, n: int = 1) -> str:
503
+ """
504
+ Randomly swap n pairs of words.
505
+
506
+ Args:
507
+ text: Input text
508
+ n: Number of swaps to perform
509
+
510
+ Returns:
511
+ Text with random swaps
512
+
513
+ Example:
514
+ >>> aug = TextAugmenter()
515
+ >>> text = "The quick brown fox"
516
+ >>> augmented = aug.random_swap(text, n=1)
517
+ """
518
+ words = text.split()
519
+
520
+ for _ in range(n):
521
+ if len(words) < 2:
522
+ break
523
+
524
+ idx1, idx2 = random.sample(range(len(words)), 2)
525
+ words[idx1], words[idx2] = words[idx2], words[idx1]
526
+
527
+ return ' '.join(words)
528
+
529
+ def random_deletion(self, text: str, p: float = 0.1) -> str:
530
+ """
531
+ Randomly delete words with probability p.
532
+
533
+ Args:
534
+ text: Input text
535
+ p: Probability of deleting each word
536
+
537
+ Returns:
538
+ Text with random deletions
539
+
540
+ Example:
541
+ >>> aug = TextAugmenter()
542
+ >>> text = "The quick brown fox jumps"
543
+ >>> augmented = aug.random_deletion(text, p=0.2)
544
+ """
545
+ words = text.split()
546
+
547
+ if len(words) == 1:
548
+ return text
549
+
550
+ new_words = [w for w in words if random.random() > p]
551
+
552
+ # If all words deleted, return random word
553
+ if len(new_words) == 0:
554
+ return random.choice(words)
555
+
556
+ return ' '.join(new_words)
557
+
558
+
559
+ class AugmentationPipeline:
560
+ """
561
+ Composable augmentation pipeline.
562
+
563
+ Chain multiple augmentation operations together.
564
+
565
+ Example:
566
+ >>> from ilovetools.ml.augmentation import ImageAugmenter, AugmentationPipeline
567
+ >>> aug = ImageAugmenter()
568
+ >>> pipeline = AugmentationPipeline([
569
+ ... lambda x: aug.random_rotation(x, max_angle=15),
570
+ ... lambda x: aug.horizontal_flip(x) if np.random.random() > 0.5 else x,
571
+ ... lambda x: aug.gaussian_noise(x, std=0.05)
572
+ ... ])
573
+ >>> image = np.random.rand(100, 100, 3)
574
+ >>> augmented = pipeline(image)
575
+ """
576
+
577
+ def __init__(self, transforms: List):
578
+ """
579
+ Initialize pipeline.
580
+
581
+ Args:
582
+ transforms: List of augmentation functions
583
+ """
584
+ self.transforms = transforms
585
+
586
+ def __call__(self, data):
587
+ """Apply all transforms in sequence."""
588
+ for transform in self.transforms:
589
+ data = transform(data)
590
+ return data
591
+
592
+ def add(self, transform):
593
+ """Add transform to pipeline."""
594
+ self.transforms.append(transform)
595
+
596
+ def remove(self, index: int):
597
+ """Remove transform at index."""
598
+ del self.transforms[index]
599
+
600
+
601
+ # Convenience functions
602
+ def rotate_image(image: np.ndarray, angle: float) -> np.ndarray:
603
+ """Rotate image by angle."""
604
+ return ImageAugmenter().rotate(image, angle)
605
+
606
+
607
+ def flip_horizontal(image: np.ndarray) -> np.ndarray:
608
+ """Flip image horizontally."""
609
+ return ImageAugmenter().horizontal_flip(image)
610
+
611
+
612
+ def add_noise(image: np.ndarray, std: float = 0.1) -> np.ndarray:
613
+ """Add Gaussian noise to image."""
614
+ return ImageAugmenter().gaussian_noise(image, std=std)
615
+
616
+
617
+ __all__ = [
618
+ 'ImageAugmenter',
619
+ 'TextAugmenter',
620
+ 'AugmentationPipeline',
621
+ 'rotate_image',
622
+ 'flip_horizontal',
623
+ 'add_noise',
624
+ ]