ch
Feedback
Machine Learning

Machine Learning

前往频道在 Telegram

Real Machine Learning — simple, practical, and built on experience. Learn step by step with clear explanations and working code. Admin: @HusseinSheikho || @Hussein_Sheikho

显示更多

📈 Telegram 频道 Machine Learning 的分析概览

频道 Machine Learning (@machinelearning9) 英语 语言赛道中的 是活跃参与者。目前社区聚集了 40 142 名订阅者,在 技术与应用 类别中位列第 3 371,并在 叙利亚 地区排名第 230

📊 受众指标与增长动态

невідомо 创建以来,项目保持高速增长,吸引了 40 142 名订阅者。

根据 26 六月, 2026 的最新数据,频道保持稳定运转。过去 30 天订阅人数变化为 429,过去 24 小时变化为 20,整体触达仍然可观。

  • 认证状态: 未认证
  • 互动率 (ER): 平均受众互动率为 1.83%。内容发布后 24 小时内通常能获得 1.60% 的反应,占订阅者总量。
  • 帖子覆盖: 每篇帖子平均可获得 735 次浏览,首日通常累积 643 次浏览。
  • 互动与反馈: 受众积极参与,单帖平均反应数为 2
  • 主题关注点: 内容集中在 distance, insidead, gpu, learning, degree 等核心主题上。

📝 描述与内容策略

作者将该频道定位为表达主观观点的平台:
Real Machine Learning — simple, practical, and built on experience. Learn step by step with clear explanations and working code. Admin: @HusseinSheikho || @Hussein_Sheikho

凭借高频更新(最新数据采集于 27 六月, 2026),频道始终保持新鲜度与高覆盖。分析显示受众积极互动,使其成为 技术与应用 类别中的关键影响点。

40 142
订阅者
+2024 小时
+1017
+42930
帖子存档
photo content

📌 Data Science Portfolios, Speeding Up Python, KANs, and Other May Must-Reads 🗂 Category: DATA SCIENCE 🕒 Date: 2024-05-30
📌 Data Science Portfolios, Speeding Up Python, KANs, and Other May Must-Reads 🗂 Category: DATA SCIENCE 🕒 Date: 2024-05-30 | ⏱️ Read time: 4 min read The stories that resonated the most with our community in the past month

📌 Orchestrating a Dynamic Time-series Pipeline in Azure 🗂 Category: DATA ENGINEERING 🕒 Date: 2024-05-31 | ⏱️ Read time: 9
📌 Orchestrating a Dynamic Time-series Pipeline in Azure 🗂 Category: DATA ENGINEERING 🕒 Date: 2024-05-31 | ⏱️ Read time: 9 min read Explore how to build, trigger, and parameterize a time-series data pipeline with ADF and Databricks,…

📌 Long Short Term Memory (LSTM)- Improving RNNs 🗂 Category: DEEP LEARNING 🕒 Date: 2024-05-31 | ⏱️ Read time: 1 min read Ho
📌 Long Short Term Memory (LSTM)- Improving RNNs 🗂 Category: DEEP LEARNING 🕒 Date: 2024-05-31 | ⏱️ Read time: 1 min read How state of the art RNNs work

📌 Long Short Term Memory (LSTM)- Improving RNNs 🗂 Category: DEEP LEARNING 🕒 Date: 2024-05-31 | ⏱️ Read time: 9 min read Ho
📌 Long Short Term Memory (LSTM)- Improving RNNs 🗂 Category: DEEP LEARNING 🕒 Date: 2024-05-31 | ⏱️ Read time: 9 min read How state of the art RNNs work

📌 TDS Newsletter: October Must-Reads on Agents, Python, Context Engineering, and More 🗂 Category: THE VARIABLE 🕒 Date: 202
📌 TDS Newsletter: October Must-Reads on Agents, Python, Context Engineering, and More 🗂 Category: THE VARIABLE 🕒 Date: 2025-10-30 | ⏱️ Read time: 3 min read A good month on TDS is one in which we get to share a wide…

📌 RF-DETR Under the Hood: The Insights of a Real-Time Transformer Detection 🗂 Category: DEEP LEARNING 🕒 Date: 2025-10-31 |
📌 RF-DETR Under the Hood: The Insights of a Real-Time Transformer Detection 🗂 Category: DEEP LEARNING 🕒 Date: 2025-10-31 | ⏱️ Read time: 6 min read From rigid grids to adaptive attention, this is the evolutionary path that made detection transformers…

# Create a copy of the original image to draw on
output_img = img.copy()

# Draw a bounding box for each detected bottle
for box in bottle_boxes:
    x1, y1, x2, y2 = map(int, box)
    # Draw a green rectangle around each bottle
    cv2.rectangle(output_img, (x1, y1), (x2, y2), (0, 255, 0), 2)

# Add the final count as text on the image
summary_text = f"Bottle Count: {bottle_count}"
cv2.putText(output_img, summary_text, (20, 50), 
            cv2.FONT_HERSHEY_SIMPLEX, 1.5, (0, 0, 255), 4)

# Save the resulting image
cv2.imwrite('factory_bottles_result.jpg', output_img)

print("Result image with detections has been saved as 'factory_bottles_result.jpg'")
--- Step 6: Discussion of Results and Limitations #Discussion #Limitations #FineTuning Result: The code successfully uses a pre-trained YOLOv8 model to identify and count standard plastic bottles in an image. The final output provides both a numerical count and a visual confirmation of the detections. Limitations of Pre-trained Model: 1. Occlusion: If bottles are heavily clustered or hiding behind each other, the model might miss some, leading to an undercount. 2. Unusual Shapes: The model is trained on common bottles (from the COCO dataset). If your factory produces bottles of a very unique shape or color, the model's accuracy might decrease. 3. Environmental Factors: Poor lighting, motion blur (if from a fast conveyor belt), or reflections can all negatively impact detection performance. How to Improve (Next Steps): For a real-world, high-accuracy industrial application, you should not rely on a generic pre-trained model. The best approach is Fine-Tuning. This involves: 1. Collecting Data: Take hundreds or thousands of pictures of your specific bottles in your actual factory environment*. 2. Annotating Data: Draw bounding boxes around every bottle in those images. 3. Training: Use this custom dataset to train (or "fine-tune") the YOLOv8 model. This teaches the model exactly what to look for in your specific use case, leading to much higher accuracy and reliability. ━━━━━━━━━━━━━━━ By: @DataScienceM

#YOLOv8 #ComputerVision #ObjectDetection #IndustrialAI #Python Applying YOLOv8 for Industrial Automation: Counting Plastic Bottles This lesson will guide you through a complete computer vision project using YOLOv8. The goal is to detect and count plastic bottles in an image from an industrial setting, such as a conveyor belt or a storage area. --- Step 1: Setup and Installation First, we need to install the necessary libraries. The ultralytics library provides the YOLOv8 model, and opencv-python is essential for image processing tasks. #Setup #Installation
# Open your terminal or command prompt and run this command:
pip install ultralytics opencv-python
--- Step 2: Loading the Model and the Target Image We will load a pre-trained YOLOv8 model. These models are trained on the large COCO dataset, which already knows how to identify common objects like 'bottle'. Then, we'll load our industrial image. Ensure you have an image named factory_bottles.jpg in your project folder. #ModelLoading #DataHandling
import cv2
from ultralytics import YOLO

# Load a pre-trained YOLOv8 model (yolov8n.pt is the smallest and fastest)
model = YOLO('yolov8n.pt')

# Load the image from the industrial setting
image_path = 'factory_bottles.jpg' # Make sure this image is in your directory
img = cv2.imread(image_path)

# A quick check to ensure the image was loaded correctly
if img is None:
    print(f"Error: Could not load image at {image_path}")
else:
    print("YOLOv8 model and image loaded successfully.")
--- Step 3: Performing Detection on the Image With the model and image loaded, we can now run the detection. The ultralytics library makes this process incredibly simple. The model will analyze the image and identify all the objects it recognizes. #Inference #ObjectDetection
# Run the model on the image to get detection results
results = model(img)

print("Detection complete. Processing results...")
--- Step 4: Filtering and Counting the Bottles The model detects many types of objects. Our task is to go through the results, filter for only the 'bottle' class, and count how many there are. We'll also store the locations (bounding boxes) of each detected bottle for visualization. #DataProcessing #Filtering
# Initialize a counter for the bottles
bottle_count = 0
bottle_boxes = []

# The model's results is a list, so we loop through it
for result in results:
    # Each result has a 'boxes' attribute with the detections
    boxes = result.boxes
    for box in boxes:
        # Get the class ID of the detected object
        class_id = int(box.cls)
        # Check if the class name is 'bottle'
        if model.names[class_id] == 'bottle':
            bottle_count += 1
            # Store the bounding box coordinates (x1, y1, x2, y2)
            bottle_boxes.append(box.xyxy[0])

print(f"Total plastic bottles detected: {bottle_count}")
--- Step 5: Visualizing the Results A number is good, but seeing what the model detected is better. We will draw the bounding boxes and the final count directly onto the image to create a clear visual output. #Visualization #OpenCV

Part 5: Training the Model We train the model using the fit() method, providing our training data, batch size, number of epochs, and validation data to monitor performance on unseen data.
history = model.fit(x_train, y_train, 
                    epochs=15, 
                    batch_size=64,
                    validation_data=(x_test, y_test))
#Training #MachineLearning #ModelFit --- Part 6: Evaluating and Discussing Results After training, we evaluate the model's performance on the test set. We also plot the training history to visualize accuracy and loss curves. This helps us understand if the model is overfitting or underfitting.
# Evaluate the model on the test data
test_loss, test_acc = model.evaluate(x_test, y_test, verbose=2)
print(f'\nTest accuracy: {test_acc:.4f}')

# Plot training & validation accuracy values
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(history.history['accuracy'])
plt.plot(history.history['val_accuracy'])
plt.title('Model accuracy')
plt.ylabel('Accuracy')
plt.xlabel('Epoch')
plt.legend(['Train', 'Test'], loc='upper left')

# Plot training & validation loss values
plt.subplot(1, 2, 2)
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('Model loss')
plt.ylabel('Loss')
plt.xlabel('Epoch')
plt.legend(['Train', 'Test'], loc='upper left')

plt.show()
Discussion: The plots show how accuracy and loss change over epochs. Ideally, both training and validation accuracy should increase, while losses decrease. If the validation accuracy plateaus or decreases while training accuracy continues to rise, it's a sign of overfitting. Our simple model achieves a decent accuracy. To improve it, one could use techniques like Data Augmentation, Dropout layers, or a deeper architecture. #Evaluation #Results #Accuracy #Overfitting --- Part 7: Making Predictions on a Single Image This is how you handle a single image file for prediction. The model expects a batch of images as input, so we must add an extra dimension to our single image before passing it to model.predict().
# Select a single image from the test set
img_index = 15
test_image = x_test[img_index]
true_label_index = np.argmax(y_test[img_index])

# Display the image
plt.imshow(test_image)
plt.title(f"Actual Label: {class_names[true_label_index]}")
plt.show()

# The model expects a batch of images, so we add a dimension
image_for_prediction = np.expand_dims(test_image, axis=0)
print("Image shape before prediction:", test_image.shape)
print("Image shape after adding batch dimension:", image_for_prediction.shape)

# Make a prediction
predictions = model.predict(image_for_prediction)
predicted_label_index = np.argmax(predictions[0])

# Print the result
print(f"\nPrediction Probabilities: {predictions[0]}")
print(f"Predicted Label: {class_names[predicted_label_index]}")
print(f"Actual Label: {class_names[true_label_index]}")
#Prediction #ImageProcessing #Inference ━━━━━━━━━━━━━━━ By: @DataScienceM

#CNN #DeepLearning #Python #Tutorial Lesson: Building a Convolutional Neural Network (CNN) for Image Classification This lesson will guide you through building a CNN from scratch using TensorFlow and Keras to classify images from the CIFAR-10 dataset. --- Part 1: Setup and Data Loading First, we import the necessary libraries and load the CIFAR-10 dataset. This dataset contains 60,000 32x32 color images in 10 classes.
import tensorflow as tf
from tensorflow.keras import datasets, layers, models
import matplotlib.pyplot as plt
import numpy as np

# Load the CIFAR-10 dataset
(x_train, y_train), (x_test, y_test) = datasets.cifar10.load_data()

# Check the shape of the data
print("Training data shape:", x_train.shape)
print("Test data shape:", x_test.shape)
#TensorFlow #Keras #DataLoading --- Part 2: Data Exploration and Preprocessing We need to prepare the data before feeding it to the network. This involves: • Normalization: Scaling pixel values from the 0-255 range to the 0-1 range. • One-Hot Encoding: Converting class vectors (integers) to a binary matrix. Let's also visualize some images to understand our data.
# Define class names for CIFAR-10
class_names = ['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']

# Visualize a few images
plt.figure(figsize=(10,10))
for i in range(25):
    plt.subplot(5,5,i+1)
    plt.xticks([])
    plt.yticks([])
    plt.grid(False)
    plt.imshow(x_train[i])
    plt.xlabel(class_names[y_train[i][0]])
plt.show()

# Normalize pixel values to be between 0 and 1
x_train = x_train.astype('float32') / 255.0
x_test = x_test.astype('float32') / 255.0

# One-hot encode the labels
y_train = tf.keras.utils.to_categorical(y_train, num_classes=10)
y_test = tf.keras.utils.to_categorical(y_test, num_classes=10)
#DataPreprocessing #Normalization #Visualization --- Part 3: Building the CNN Model Now, we'll construct our CNN model. A common architecture consists of a stack of Conv2D and MaxPooling2D layers, followed by Dense layers for classification. • Conv2D: Extracts features (like edges, corners) from the input image. • MaxPooling2D: Reduces the spatial dimensions (downsampling), which helps in making the feature detection more robust. • Flatten: Converts the 2D feature maps into a 1D vector. • Dense: A standard fully-connected neural network layer.
model = models.Sequential()

# Convolutional Base
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3)))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))

# Flatten and Dense Layers
model.add(layers.Flatten())
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(10, activation='softmax')) # 10 output classes

# Print the model summary
model.summary()
#ModelBuilding #CNN #KerasLayers --- Part 4: Compiling the Model Before training, we need to configure the learning process. This is done via the compile() method, which requires: • Optimizer: An algorithm to update the model's weights (e.g., 'adam'). • Loss Function: A function to measure how inaccurate the model is during training (e.g., 'categorical_crossentropy' for multi-class classification). • Metrics: Used to monitor the training and testing steps (e.g., 'accuracy').
model.compile(optimizer='adam',
              loss='categorical_crossentropy',
              metrics=['accuracy'])
#ModelCompilation #Optimizer #LossFunction ---

Clean Code Tip: When building complex architectures like ResNets, defining skip connections directly in the main forward method leads to repetitive, hard-to-read code. Encapsulate repeating patterns, like a residual block, into their own reusable nn.Module. This promotes modularity, follows the DRY principle, and makes your overall network architecture dramatically cleaner. 🧱 Example:
import torch
import torch.nn as nn

# --- The Cluttered, Repetitive Way ---
class ClutteredResNet(nn.Module):
    def __init__(self, in_channels=64, num_classes=10):
        super().__init__()
        # Defining layers for two blocks inline... gets messy fast.
        self.conv1a = nn.Conv2d(in_channels, 64, 3, padding=1)
        self.bn1a = nn.BatchNorm2d(64)
        self.conv1b = nn.Conv2d(64, 64, 3, padding=1)
        self.bn1b = nn.BatchNorm2d(64)
        
        self.conv2a = nn.Conv2d(64, 64, 3, padding=1)
        self.bn2a = nn.BatchNorm2d(64)
        self.conv2b = nn.Conv2d(64, 64, 3, padding=1)
        self.bn2b = nn.BatchNorm2d(64)
        
        self.relu = nn.ReLU(inplace=True)
        # ...imagine more blocks...

    def forward(self, x):
        # Manually implementing the first block's logic
        identity1 = x
        out = self.relu(self.bn1a(self.conv1a(x)))
        out = self.bn1b(self.conv1b(out))
        out += identity1 # The skip connection
        out = self.relu(out)
        
        # Repeating the same logic for the second block
        identity2 = out
        out = self.relu(self.bn2a(self.conv2a(out)))
        out = self.bn2b(self.conv2b(out))
        out += identity2 # Another skip connection
        out = self.relu(out)
        return out


# --- The Clean, Modular Way ---

# 1. First, create a reusable module for the repeating block
class ResidualBlock(nn.Module):
    def __init__(self, in_channels, out_channels):
        super().__init__()
        self.conv1 = nn.Conv2d(in_channels, out_channels, 3, padding=1)
        self.bn1 = nn.BatchNorm2d(out_channels)
        self.conv2 = nn.Conv2d(out_channels, out_channels, 3, padding=1)
        self.bn2 = nn.BatchNorm2d(out_channels)
        self.relu = nn.ReLU(inplace=True)

    def forward(self, x):
        identity = x
        out = self.relu(self.bn1(self.conv1(x)))
        out = self.bn2(self.conv2(out))
        out += identity # Encapsulated skip connection logic
        out = self.relu(out)
        return out

# 2. Then, compose the main model from these clean blocks
class CleanResNet(nn.Module):
    def __init__(self, in_channels=64, num_classes=10):
        super().__init__()
        # The architecture is now clear and declarative
        self.layer1 = ResidualBlock(in_channels, 64)
        self.layer2 = ResidualBlock(64, 64)
        # ... add more blocks easily ...

    def forward(self, x):
        # The forward pass is simple and readable
        x = self.layer1(x)
        x = self.layer2(x)
        return x

print("--- Clean Model Architecture ---")
model = CleanResNet()
print(model)
━━━━━━━━━━━━━━━ By: @DataScienceM

📌 The Machine Learning Projects Employers Want to See 🗂 Category: MACHINE LEARNING 🕒 Date: 2025-10-31 | ⏱️ Read time: 7 mi
📌 The Machine Learning Projects Employers Want to See 🗂 Category: MACHINE LEARNING 🕒 Date: 2025-10-31 | ⏱️ Read time: 7 min read What machine learning projects will actually get you interviews and jobs

Clean Code Tip: For sequential CNN architectures, defining layers individually and calling them one-by-one in the forward method creates boilerplate. Encapsulate your network trunk in an nn.Sequential container. This makes your architecture declarative, compact, and much easier to read at a glance. 🏗️ Example:
import torch
import torch.nn as nn

# --- The Verbose, Repetitive Way ---
class VerboseCNN(nn.Module):
    def __init__(self, num_classes=10):
        super().__init__()
        # Layers are defined one by one
        self.conv1 = nn.Conv2d(1, 16, kernel_size=3, padding=1)
        self.relu1 = nn.ReLU()
        self.pool1 = nn.MaxPool2d(2)
        self.conv2 = nn.Conv2d(16, 32, kernel_size=3, padding=1)
        self.relu2 = nn.ReLU()
        self.pool2 = nn.MaxPool2d(2)
        self.flatten = nn.Flatten()
        self.fc = nn.Linear(32 * 7 * 7, num_classes)

    def forward(self, x):
        # The forward pass is a long, manual chain of calls
        x = self.conv1(x)
        x = self.relu1(x)
        x = self.pool1(x)
        x = self.conv2(x)
        x = self.relu2(x)
        x = self.pool2(x)
        x = self.flatten(x)
        x = self.fc(x)
        return x

print("--- Verbose Way ---")
verbose_model = VerboseCNN()
print(verbose_model)


# --- The Clean, Declarative Way with nn.Sequential ---
class CleanCNN(nn.Module):
    def __init__(self, num_classes=10):
        super().__init__()
        # The feature extractor is a clean, sequential block
        self.features = nn.Sequential(
            nn.Conv2d(1, 16, kernel_size=3, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(2),
            nn.Conv2d(16, 32, kernel_size=3, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(2),
            nn.Flatten()
        )
        self.classifier = nn.Linear(32 * 7 * 7, num_classes)

    def forward(self, x):
        # The forward pass is simple and clear
        features = self.features(x)
        output = self.classifier(features)
        return output

print("\n--- Clean Way ---")
clean_model = CleanCNN()
print(clean_model)
━━━━━━━━━━━━━━━ By: @DataScienceM

Clean Code Tip: Instead of creating messy intermediate DataFrames for each step of a transformation, use method chaining. For custom or complex operations that don't have a built-in method, use .pipe() to insert your own functions without breaking the chain. This creates a clean, readable, and reproducible data processing pipeline. ⛓️ Example:
import pandas as pd

# Sample data
data = {
    'region': ['North', 'South', 'North', 'South', 'East', 'West'],
    'product': ['A', 'A', 'B', 'B', 'A', 'B'],
    'sales': [100, 150, 200, 50, 300, 220],
    'cost': [80, 120, 150, 40, 210, 180]
}
df = pd.DataFrame(data)

# A custom function to apply a regional surcharge
def apply_surcharge(dataframe, region, surcharge_percent):
    df_copy = dataframe.copy()
    surcharge_rate = 1 + (surcharge_percent / 100)
    mask = df_copy['region'] == region
    df_copy.loc[mask, 'profit'] *= surcharge_rate
    return df_copy

# --- The Old, Step-by-Step Way ---
print("--- Old Way ---")
# Step 1: Filter out East and West regions
df1 = df[df['region'].isin(['North', 'South'])]
# Step 2: Calculate profit
df2 = df1.assign(profit=df1['sales'] - df1['cost'])
# Step 3: Apply the custom surcharge logic, breaking the flow
df3 = apply_surcharge(df2, region='North', surcharge_percent=5)
# Step 4: Aggregate the results
old_result = df3.groupby('region')['profit'].sum().round(2)
print(old_result)


# --- The Clean, Chained Way using .pipe() ---
print("\n--- Clean Way ---")
clean_result = (
    df
    .query("region in ['North', 'South']")
    .assign(profit=lambda d: d['sales'] - d['cost'])
    .pipe(apply_surcharge, region='North', surcharge_percent=5)
    .groupby('region')['profit']
    .sum()
    .round(2)
)
print(clean_result)
━━━━━━━━━━━━━━━ By: @DataScienceM

📌 Let Hypothesis Break Your Python Code Before Your Users Do 🗂 Category: PROGRAMMING 🕒 Date: 2025-10-31 | ⏱️ Read time: 19
📌 Let Hypothesis Break Your Python Code Before Your Users Do 🗂 Category: PROGRAMMING 🕒 Date: 2025-10-31 | ⏱️ Read time: 19 min read Property-based tests that find bugs you didn’t know existed.

📌 Introduction to spatial analysis of cells for neuroscientists (part 1) 🗂 Category: DATA SCIENCE 🕒 Date: 2024-05-30 | ⏱️
📌 Introduction to spatial analysis of cells for neuroscientists (part 1) 🗂 Category: DATA SCIENCE 🕒 Date: 2024-05-30 | ⏱️ Read time: 10 min read An approach using point patterns analysis (PPA) with spatstat

📌 Build Your Own ChatGPT-like Chatbot with Java and Python 🗂 Category: ARTIFICIAL INTELLIGENCE 🕒 Date: 2024-05-30 | ⏱️ Rea
📌 Build Your Own ChatGPT-like Chatbot with Java and Python 🗂 Category: ARTIFICIAL INTELLIGENCE 🕒 Date: 2024-05-30 | ⏱️ Read time: 33 min read Creating a custom LLM inference infrastructure from scratch

📌 Scalable OCR Pipelines using AWS 🗂 Category: SOFTWARE ENGINEERING 🕒 Date: 2024-05-30 | ⏱️ Read time: 13 min read A surve
📌 Scalable OCR Pipelines using AWS 🗂 Category: SOFTWARE ENGINEERING 🕒 Date: 2024-05-30 | ⏱️ Read time: 13 min read A survey of 3 different OCR pipeline patterns and their pros and cons

📌 N-HiTS – Making Deep Learning for Time Series Forecasting More Efficient 🗂 Category: DATA SCIENCE 🕒 Date: 2024-05-30 | ⏱
📌 N-HiTS – Making Deep Learning for Time Series Forecasting More Efficient 🗂 Category: DATA SCIENCE 🕒 Date: 2024-05-30 | ⏱️ Read time: 11 min read A deep dive into how N-HiTS works and how you can use it