CANN内存管理与资源优化

引言

内存管理是深度学习推理性能的关键因素之一。CANN提供了丰富的内存管理机制和优化策略,通过合理的内存分配、复用和优化,可以显著提升推理性能,降低内存占用,提高资源利用率。

本文将深入介绍CANN的内存管理机制和资源优化方法,帮助开发者构建高效的内存管理方案。

相关链接:

一、CANN内存架构

1.1 内存层次结构

CANN的内存系统采用多层次架构,不同层次的内存具有不同的访问速度和容量。

python 复制代码
def memory_hierarchy_demo():
    """CANN内存层次结构"""
    print("CANN内存层次结构")
    print("=" * 50)

    print("\n1. Host内存(主机内存)")
    print("   - 位置: CPU端")
    print("   - 速度: 相对较慢")
    print("   - 容量: 大(数十GB)")
    print("   - 用途: 数据存储、预处理")

    print("\n2. Device内存(设备内存)")
    print("   - 位置: 加速器端")
    print("   - 速度: 快")
    print("   - 容量: 中等(数GB到数十GB)")
    print("   - 用途: 模型存储、计算数据")

    print("\n3. Unified Memory(统一内存)")
    print("   - 位置: Host和Device共享")
    print("   - 速度: 中等")
    print("   - 容量: 大")
    print("   - 用途: 简化编程,自动迁移")

    print("\n4. 缓存(Cache)")
    print("   - 位置: 硬件内部")
    print("   - 速度: 最快")
    print("   - 容量: 小(KB到MB)")
    print("   - 用途: 热点数据缓存")

    print("=" * 50)

memory_hierarchy_demo()
1.2 内存访问模式
python 复制代码
def memory_access_patterns():
    """内存访问模式"""
    print("内存访问模式")
    print("=" * 50)

    patterns = [
        "1. 顺序访问",
        "   - 特点: 连续访问内存",
        "   - 性能: 高(缓存命中率高)",
        "   - 适用: 矩阵运算、卷积操作",
        "",
        "2. 随机访问",
        "   - 特点: 非连续访问内存",
        "   - 性能: 低(缓存命中率低)",
        "   - 适用: 稀疏操作、索引访问",
        "",
        "3. 流式访问",
        "   - 特点: 单向连续访问",
        "   - 性能: 高(预取效果好)",
        "   - 适用: 数据加载、结果输出",
        "",
        "4. 重访问",
        "   - 特点: 重复访问同一数据",
        "   - 性能: 取决于缓存策略",
        "   - 适用: 迭代计算、累积操作"
    ]

    for pattern in patterns:
        print(pattern)

    print("\n优化建议:")
    print("- 尽量使用顺序访问")
    print("- 提高数据局部性")
    print("- 减少随机访问")
    print("- 利用缓存机制")

    print("=" * 50)

memory_access_patterns()

二、内存分配与释放

2.1 基础内存操作
python 复制代码
import acl
import numpy as np

class CANNMemoryManager:
    """CANN内存管理器"""
    def __init__(self, device_id=0):
        self.device_id = device_id
        ret = acl.init()
        ret = acl.rt.set_device(device_id)

    def malloc_host(self, size):
        """分配Host内存"""
        ptr, ret = acl.rt.malloc_host(size)
        if ret != 0:
            raise RuntimeError(f"Host内存分配失败: {ret}")
        return ptr

    def malloc_device(self, size):
        """分配Device内存"""
        ptr, ret = acl.rt.malloc(size, 0)
        if ret != 0:
            raise RuntimeError(f"Device内存分配失败: {ret}")
        return ptr

    def free_host(self, ptr):
        """释放Host内存"""
        ret = acl.rt.free_host(ptr)
        if ret != 0:
            raise RuntimeError(f"Host内存释放失败: {ret}")

    def free_device(self, ptr):
        """释放Device内存"""
        ret = acl.rt.free(ptr)
        if ret != 0:
            raise RuntimeError(f"Device内存释放失败: {ret}")

    def memcpy_h2d(self, device_ptr, host_ptr, size):
        """Host到Device数据传输"""
        ret = acl.rt.memcpy(device_ptr, size,
                           host_ptr, size,
                           acl.rt.MEMCPY_HOST_TO_DEVICE)
        if ret != 0:
            raise RuntimeError(f"数据传输失败: {ret}")

    def memcpy_d2h(self, host_ptr, device_ptr, size):
        """Device到Host数据传输"""
        ret = acl.rt.memcpy(host_ptr, size,
                           device_ptr, size,
                           acl.rt.MEMCPY_DEVICE_TO_HOST)
        if ret != 0:
            raise RuntimeError(f"数据传输失败: {ret}")

# 使用示例
mem_manager = CANNMemoryManager(device_id=0)

# 分配内存
data = np.random.rand(1024, 1024).astype(np.float32)
host_ptr = mem_manager.malloc_host(data.nbytes)
device_ptr = mem_manager.malloc_device(data.nbytes)

# 数据传输
mem_manager.memcpy_h2d(device_ptr, host_ptr, data.nbytes)

# ... 执行计算 ...

# 释放内存
mem_manager.free_host(host_ptr)
mem_manager.free_device(device_ptr)
2.2 内存对齐
python 复制代码
def aligned_memory_allocation():
    """内存对齐分配"""
    print("内存对齐优化")
    print("=" * 50)

    print("\n1. 为什么需要内存对齐:")
    reasons = [
        "- 硬件要求: 某些硬件要求内存对齐",
        "- 访问效率: 对齐的内存访问更快",
        "- 原子操作: 某些操作需要内存对齐",
        "- DMA传输: DMA通常需要对齐的内存"
    ]

    for reason in reasons:
        print(f"   {reason}")

    print("\n2. 常见对齐要求:")
    alignments = [
        "- 32字节对齐: SIMD操作",
        "- 64字节对齐: 缓存行大小",
        "- 128字节对齐: 某些加速器",
        "- 512字节对齐: 大块数据传输"
    ]

    for align in alignments:
        print(f"   {align}")

    print("\n3. CANN内存对齐:")
    print("- acl.rt.malloc()自动对齐")
    print("- 通常对齐到32或64字节")
    print("- 确保硬件最优访问")

    print("=" * 50)

aligned_memory_allocation()

三、内存复用策略

3.1 内存池实现
python 复制代码
import threading

class MemoryPool:
    """内存池"""
    def __init__(self, device_id=0):
        self.device_id = device_id
        self.pools = {}  # {size: [(ptr, used_timestamp), ...]}
        self.lock = threading.Lock()
        self.max_pool_size = 1000  # 最大池大小

    def allocate(self, size):
        """从池中分配内存"""
        with self.lock:
            # 尝试从池中获取
            pool_key = self._get_pool_key(size)
            if pool_key in self.pools and self.pools[pool_key]:
                ptr, _ = self.pools[pool_key].pop()
                return ptr

            # 池中没有,分配新的
            ptr, ret = acl.rt.malloc(size, 0)
            if ret != 0:
                raise RuntimeError(f"内存分配失败: {ret}")

            return ptr

    def deallocate(self, ptr, size):
        """归还内存到池中"""
        with self.lock:
            pool_key = self._get_pool_key(size)

            if pool_key not in self.pools:
                self.pools[pool_key] = []

            # 检查池大小
            if len(self.pools[pool_key]) >= self.max_pool_size:
                # 池已满,直接释放
                acl.rt.free(ptr)
            else:
                # 归还到池中
                self.pools[pool_key].append((ptr, time.time()))

    def _get_pool_key(self, size):
        """获取池的键值(按大小分组)"""
        # 将大小对齐到最近的2的幂
        aligned_size = 1
        while aligned_size < size:
            aligned_size <<= 1
        return aligned_size

    def clear(self):
        """清空内存池"""
        with self.lock:
            for pool_key, blocks in self.pools.items():
                for ptr, _ in blocks:
                    acl.rt.free(ptr)
            self.pools.clear()

# 使用示例
memory_pool = MemoryPool(device_id=0)

# 分配内存
data = np.random.rand(1024, 1024).astype(np.float32)
ptr1 = memory_pool.allocate(data.nbytes)
ptr2 = memory_pool.allocate(data.nbytes)

# 使用内存...

# 归还内存
memory_pool.deallocate(ptr1, data.nbytes)
memory_pool.deallocate(ptr2, data.nbytes)

# 清空池
memory_pool.clear()
3.2 中间结果复用
python 复制代码
class IntermediateBufferPool:
    """中间结果缓冲池"""
    def __init__(self, device_id=0):
        self.device_id = device_id
        self.buffers = {}  # {shape: [(ptr, ref_count), ...]}
        self.lock = threading.Lock()

    def get_buffer(self, shape, dtype=np.float32):
        """获取缓冲区"""
        size = np.prod(shape) * np.dtype(dtype).itemsize
        buffer_key = (shape, dtype)

        with self.lock:
            if buffer_key in self.buffers and self.buffers[buffer_key]:
                ptr, ref_count = self.buffers[buffer_key].pop()
                # 增加引用计数
                return ptr, ref_count + 1

            # 分配新缓冲区
            ptr, ret = acl.rt.malloc(size, 0)
            if ret != 0:
                raise RuntimeError(f"缓冲区分配失败: {ret}")

            return ptr, 1

    def release_buffer(self, ptr, shape, dtype=np.float32, ref_count=1):
        """释放缓冲区"""
        buffer_key = (shape, dtype)

        with self.lock:
            if ref_count > 1:
                # 仍有引用,减计数
                self.buffers[buffer_key].append((ptr, ref_count - 1))
            else:
                # 没有引用,归还到池中
                if buffer_key not in self.buffers:
                    self.buffers[buffer_key] = []
                self.buffers[buffer_key].append((ptr, 0))

    def clear(self):
        """清空缓冲池"""
        with self.lock:
            for buffer_key, buffers in self.buffers.items():
                for ptr, _ in buffers:
                    acl.rt.free(ptr)
            self.buffers.clear()

四、内存优化技术

4.1 零拷贝优化
python 复制代码
def zero_copy_demo():
    """零拷贝优化"""
    print("零拷贝优化技术")
    print("=" * 50)

    print("\n1. 什么是零拷贝:")
    print("   - 直接访问数据,无需复制")
    print("   - 减少内存带宽消耗")
    print("   - 降低CPU开销")

    print("\n2. CANN零拷贝技术:")
    techniques = [
        "a) Device直接访问",
        "   - 在Device端处理数据",
        "   - 避免Host和Device间传输",
        "   - 使用acl.rt.malloc()直接分配",

        "b) Pinned Memory",
        "   - 使用页锁定内存",
        "   - DMA直接传输",
        "   - 使用acl.rt.malloc_host()",

        "c) Shared Memory",
        "   - Host和Device共享内存",
        "   - 减少数据复制",
        "   - 适合小数据频繁交互"
    ]

    for tech in techniques:
        print(f"\n{tech}")

    print("\n3. 使用示例:")
    code = """
# 使用Pinned Memory实现零拷贝
host_ptr = acl.rt.malloc_host(size)
device_ptr = acl.rt.malloc(size, 0)

# DMA直接传输,无需CPU拷贝
acl.rt.memcpy(device_ptr, size, host_ptr, size,
              acl.rt.MEMCPY_HOST_TO_DEVICE)
"""
    print(code)

    print("\n4. 性能提升:")
    print("- 减少CPU负载")
    print("- 提高传输速度")
    print("- 降低功耗")

    print("=" * 50)

zero_copy_demo()
4.2 内存预分配
python 复制代码
class PreallocatedMemoryManager:
    """预分配内存管理器"""
    def __init__(self, device_id=0, sizes=None):
        self.device_id = device_id
        self.memory_blocks = {}  # {size: [ptr1, ptr2, ...]}

        # 预分配内存
        if sizes:
            for size in sizes:
                count = max(10, size // (1024 * 1024))  # 每MB至少10个块
                for _ in range(count):
                    ptr, ret = acl.rt.malloc(size, 0)
                    if ret == 0:
                        if size not in self.memory_blocks:
                            self.memory_blocks[size] = []
                        self.memory_blocks[size].append(ptr)

    def allocate(self, size):
        """分配内存"""
        # 找到合适的预分配块
        for pool_size in sorted(self.memory_blocks.keys()):
            if pool_size >= size:
                if self.memory_blocks[pool_size]:
                    ptr = self.memory_blocks[pool_size].pop()
                    return ptr

        # 没有合适的预分配块,分配新的
        ptr, ret = acl.rt.malloc(size, 0)
        if ret != 0:
            raise RuntimeError(f"内存分配失败: {ret}")
        return ptr

    def deallocate(self, ptr, size):
        """释放内存"""
        # 检查是否有对应大小的池
        if size in self.memory_blocks:
            self.memory_blocks[size].append(ptr)
        else:
            acl.rt.free(ptr)

    def get_memory_info(self):
        """获取内存信息"""
        info = {}
        for size, blocks in self.memory_blocks.items():
            info[size] = {
                'total_blocks': len(blocks),
                'available_blocks': len(blocks),
                'total_size': size * len(blocks)
            }
        return info

# 使用示例
sizes = [1024, 4096, 16384, 65536, 262144, 1048576]  # 常用大小
mem_manager = PreallocatedMemoryManager(device_id=0, sizes=sizes)

# 快速分配内存
ptr1 = mem_manager.allocate(1024)
ptr2 = mem_manager.allocate(5000)  # 使用4096的块

# 释放内存
mem_manager.deallocate(ptr1, 1024)
mem_manager.deallocate(ptr2, 5000)

# 查看内存信息
print(mem_manager.get_memory_info())

五、动态内存管理

5.1 动态Shape内存管理
python 复制代码
class DynamicShapeMemoryManager:
    """动态Shape内存管理器"""
    def __init__(self, device_id=0):
        self.device_id = device_id
        self.shape_history = {}  # {shape_tuple: count}
        self.memory_cache = {}  # {shape: ptr}
        self.lock = threading.Lock()

    def allocate_for_shape(self, shape, dtype=np.float32):
        """为特定Shape分配内存"""
        size = np.prod(shape) * np.dtype(dtype).itemsize
        shape_key = tuple(shape)

        with self.lock:
            # 记录Shape使用频率
            if shape_key not in self.shape_history:
                self.shape_history[shape_key] = 0
            self.shape_history[shape_key] += 1

            # 检查是否有缓存的内存
            if shape_key in self.memory_cache:
                return self.memory_cache[shape_key]

            # 分配新内存
            ptr, ret = acl.rt.malloc(size, 0)
            if ret != 0:
                raise RuntimeError(f"内存分配失败: {ret}")

            # 缓存常用Shape的内存
            if self.shape_history[shape_key] > 5:  # 使用超过5次才缓存
                self.memory_cache[shape_key] = ptr

            return ptr

    def get_shape_statistics(self):
        """获取Shape统计信息"""
        with self.lock:
            sorted_shapes = sorted(
                self.shape_history.items(),
                key=lambda x: x[1],
                reverse=True
            )
            return sorted_shapes[:10]  # 返回前10个常用Shape

    def clear_cache(self):
        """清空缓存"""
        with self.lock:
            for ptr in self.memory_cache.values():
                acl.rt.free(ptr)
            self.memory_cache.clear()

# 使用示例
shape_manager = DynamicShapeMemoryManager(device_id=0)

# 处理不同Shape的输入
shapes = [(1, 3, 224, 224), (1, 3, 320, 320), (1, 3, 640, 640)]
for shape in shapes:
    ptr = shape_manager.allocate_for_shape(shape)
    # 使用内存...
    print(f"为Shape {shape} 分配内存")

# 查看常用Shape
print("常用Shape统计:")
print(shape_manager.get_shape_statistics())
5.2 内存碎片整理
python 复制代码
def memory_defragmentation():
    """内存碎片整理"""
    print("内存碎片整理")
    print("=" * 50)

    print("\n1. 内存碎片问题:")
    problems = [
        "   - 频繁分配释放导致碎片",
        "   - 降低内存利用率",
        "   - 影响分配性能",
        "   - 可能导致分配失败"
    ]

    for problem in problems:
        print(problem)

    print("\n2. 碎片整理策略:")
    strategies = [
        "   a) 定期整理",
        "   - 设置整理周期",
        "   - 在低负载时执行",
        "   - 释放并重新分配",

        "   b) 内存池",
        "   - 预分配大块内存",
        "   - 内部管理分配",
        "   - 减少外部碎片",

        "   c) 分配策略",
        "   - 使用固定大小块",
        "   - 对齐分配",
        "   - 避免频繁释放"
    ]

    for strategy in strategies:
        print(strategy)

    print("\n3. 实施建议:")
    recommendations = [
        "   - 使用内存池减少碎片",
        "   - 定期监控内存使用",
        "   - 在应用启动时预分配",
        "   - 避免频繁的小块分配"
    ]

    for rec in recommendations:
        print(rec)

    print("=" * 50)

memory_defragmentation()

六、内存监控与调试

6.1 内存使用监控
python 复制代码
class MemoryMonitor:
    """内存监控器"""
    def __init__(self, device_id=0):
        self.device_id = device_id
        self.allocations = {}  # {ptr: {'size': size, 'timestamp': time, 'tag': tag}}
        self.peak_memory = 0
        self.current_memory = 0
        self.lock = threading.Lock()

    def record_allocation(self, ptr, size, tag=""):
        """记录内存分配"""
        with self.lock:
            self.allocations[ptr] = {
                'size': size,
                'timestamp': time.time(),
                'tag': tag
            }
            self.current_memory += size
            self.peak_memory = max(self.peak_memory, self.current_memory)

    def record_deallocation(self, ptr):
        """记录内存释放"""
        with self.lock:
            if ptr in self.allocations:
                size = self.allocations[ptr]['size']
                del self.allocations[ptr]
                self.current_memory -= size

    def get_memory_info(self):
        """获取内存信息"""
        with self.lock:
            return {
                'current_memory': self.current_memory,
                'peak_memory': self.peak_memory,
                'allocation_count': len(self.allocations),
                'allocations': list(self.allocations.values())
            }

    def print_memory_summary(self):
        """打印内存摘要"""
        info = self.get_memory_info()
        print("\n内存使用摘要:")
        print(f"  当前内存: {info['current_memory'] / 1024**2:.2f} MB")
        print(f"  峰值内存: {info['peak_memory'] / 1024**2:.2f} MB")
        print(f"  分配数量: {info['allocation_count']}")

        if info['allocation_count'] > 0:
            print("\n  最大的10个分配:")
            sorted_allocs = sorted(
                info['allocations'],
                key=lambda x: x['size'],
                reverse=True
            )[:10]
            for alloc in sorted_allocs:
                print(f"    {alloc['size'] / 1024**2:.2f} MB - {alloc['tag']}")

# 使用示例
memory_monitor = MemoryMonitor(device_id=0)

# 分配内存时记录
ptr1 = acl.rt.malloc(1024*1024, 0)
memory_monitor.record_allocation(ptr1, 1024*1024, tag="buffer1")

ptr2 = acl.rt.malloc(2048*1024, 0)
memory_monitor.record_allocation(ptr2, 2048*1024, tag="buffer2")

# 释放内存时记录
memory_monitor.record_deallocation(ptr1)
acl.rt.free(ptr1)

# 查看内存信息
memory_monitor.print_memory_summary()
6.2 内存泄漏检测
python 复制代码
class MemoryLeakDetector:
    """内存泄漏检测器"""
    def __init__(self):
        self.allocations = {}
        self.leak_threshold = 10  # 分配次数阈值

    def track_allocation(self, ptr, size, location=""):
        """跟踪内存分配"""
        stack = traceback.extract_stack()
        caller = str(stack[-2]) if len(stack) > 1 else ""

        if ptr not in self.allocations:
            self.allocations[ptr] = {
                'size': size,
                'alloc_count': 0,
                'free_count': 0,
                'locations': [],
                'last_alloc_time': time.time()
            }

        alloc_info = self.allocations[ptr]
        alloc_info['alloc_count'] += 1
        alloc_info['last_alloc_time'] = time.time()
        if location:
            alloc_info['locations'].append(location)
        if caller:
            alloc_info['locations'].append(caller)

    def track_deallocation(self, ptr):
        """跟踪内存释放"""
        if ptr in self.allocations:
            self.allocations[ptr]['free_count'] += 1

    def detect_leaks(self):
        """检测内存泄漏"""
        leaks = []

        for ptr, info in self.allocations.items():
            # 检查分配和释放次数不平衡
            if info['alloc_count'] > info['free_count'] + self.leak_threshold:
                leaks.append({
                    'ptr': ptr,
                    'size': info['size'],
                    'alloc_count': info['alloc_count'],
                    'free_count': info['free_count'],
                    'leak_count': info['alloc_count'] - info['free_count'],
                    'locations': info['locations']
                })

        return leaks

    def print_leak_report(self):
        """打印泄漏报告"""
        leaks = self.detect_leaks()

        if not leaks:
            print("未检测到内存泄漏")
            return

        print(f"\n检测到 {len(leaks)} 处潜在的内存泄漏:")
        for i, leak in enumerate(leaks, 1):
            print(f"\n泄漏 #{i}:")
            print(f"  指针: 0x{leak['ptr']:X}")
            print(f"  大小: {leak['size']} bytes")
            print(f"  分配次数: {leak['alloc_count']}")
            print(f"  释放次数: {leak['free_count']}")
            print(f"  泄漏次数: {leak['leak_count']}")
            print(f"  分配位置:")
            for loc in leak['locations'][-5:]:  # 显示最近5个位置
                print(f"    {loc}")

# 使用示例
leak_detector = MemoryLeakDetector()

# 分配内存时跟踪
ptr = acl.rt.malloc(1024*1024, 0)
leak_detector.track_allocation(ptr, 1024*1024, location="main:100")

# 检测泄漏
leak_detector.print_leak_report()

七、内存优化最佳实践

7.1 优化建议
python 复制代码
def memory_optimization_best_practices():
    """内存优化最佳实践"""
    print("内存优化最佳实践")
    print("=" * 50)

    practices = [
        "1. 预分配内存",
        "   - 在应用启动时分配所需内存",
        "   - 避免运行时频繁分配",
        "   - 使用内存池管理",

        "",
        "2. 及时释放",
        "   - 不再使用的内存立即释放",
        "   - 避免内存泄漏",
        "   - 使用RAII模式",

        "",
        "3. 复用内存",
        "   - 中间结果复用",
        "   - 模型参数共享",
        "   - 批处理内存复用",

        "",
        "4. 对齐分配",
        "   - 按照硬件要求对齐",
        "   - 提升访问效率",
        "   - 避免性能损失",

        "",
        "5. 监控使用",
        "   - 实时监控内存使用",
        "   - 记录峰值内存",
        "   - 检测内存泄漏",

        "",
        "6. 合理规划",
        "   - 估算内存需求",
        "   - 留出余量",
        "   - 分配策略优化"
    ]

    for practice in practices:
        print(practice)

    print("=" * 50)

memory_optimization_best_practices()
7.2 常见问题
python 复制代码
def common_memory_issues():
    """常见内存问题及解决方案"""
    print("常见内存问题及解决方案")
    print("=" * 50)

    issues = [
        "问题1: 内存不足",
        "   原因: Batch Size过大,模型参数多",
        "   解决: 减小Batch Size,使用量化,模型压缩",

        "",
        "问题2: 内存泄漏",
        "   原因: 分配后未释放,引用计数错误",
        "   解决: 使用内存监控,及时释放,检查代码",

        "",
        "问题3: 内存碎片",
        "   原因: 频繁分配释放,大小不一",
        "   解决: 使用内存池,预分配,定期整理",

        "",
        "问题4: 性能低下",
        "   原因: 频繁传输,未对齐,缓存未命中",
        "   解决: 减少传输,内存对齐,优化访问模式",

        "",
        "问题5: 分配失败",
        "   原因: 内存不足,碎片严重",
        "   解决: 释放内存,整理碎片,增加内存"
    ]

    for issue in issues:
        print(issue)

    print("=" * 50)

common_memory_issues()

总结

本文详细介绍了CANN内存管理与资源优化的各个方面,包括:

  • CANN内存架构和访问模式
  • 内存分配与释放
  • 内存复用策略
  • 内存优化技术
  • 动态内存管理
  • 内存监控与调试
  • 内存优化最佳实践

通过合理的内存管理和优化,可以显著提升CANN应用的性能,降低资源消耗,提高系统稳定性。建议开发者根据具体应用场景,选择合适的内存管理策略,并持续监控和优化内存使用。

相关链接:

相关推荐
xixixi777773 分钟前
英伟达Agent专用全模态模型出击,仿冒AI智能体泛滥成灾,《AI伦理安全指引》即将落地——AI治理迎来“技术-风险-规范”三重奏
人工智能·5g·安全·ai·大模型·英伟达·智能体
直奔標竿5 分钟前
Java开发者AI转型第二十六课!Spring AI 个人知识库实战(五)——联网搜索增强实战
java·开发语言·人工智能·spring boot·后端·spring
数据皮皮侠AI8 分钟前
中国城市可再生能源数据集(2005-2021)|顶刊 Sci Data 11 种能源面板
大数据·人工智能·笔记·能源·1024程序员节
G311354227313 分钟前
如何用 QClaw 龙虾做一个规律作息健康助理 Agent
大数据·人工智能·ai·云计算
幂律智能14 分钟前
零售行业合同管理数智化转型解决方案
大数据·人工智能·零售
旺财矿工15 分钟前
零基础搭建 OpenClaw 2.6.6 Win11 本地化运行环境
人工智能·openclaw·小龙虾·龙虾·openclaw安装包
九成宫16 分钟前
动手学深度学习PyTorch版初步安装过程
人工智能·pytorch·深度学习
Traving Yu17 分钟前
Prompt提示词工程
人工智能·prompt
NOCSAH17 分钟前
统好AI CRM功能解析:智能录入与跟进
人工智能
He少年19 分钟前
【AI 辅助编程做设备数据采集:一个真实项目的迭代复盘(OpenSpec 驱动)】
人工智能